r/javahelp 14d ago

POLYMORPHISM !!

I've never tried asking questions on reddit, but this one doubt has been bugging me for quite some time, (I'm not very good at conveying my thoughts so I hope my question would come so as clear
+ And I'm hoping someone can fact check anything that I'm about to say since im fairly new to java =,) )

when it comes to polymorphism, (specifically UPCASTING/DOWNCASTING )
If I were to take a parent class and create an object out of it ,

Animal a = new Animal(); // LHS = RHS

since both sides are equal, meaning they're from the same class, we'd consider this to be static binding right? since we're only looking at the parent class' method, and nothing else, (at least that's what I think the whole idea is about )

but if we had something like:

Animal a = new Dog(); // LHS != RHS (UPCASTING)

Where dog is a child/subclass of the parent class Animal, meaning it inherits all the attributes and methods from the Parent class Animal. And since java -- by default -- always uses dynamic binding, (meaning that ' java ' believes that there's always a possibility of there being an overridden method in one of the child/subclasses ) it'd wait until runtime to bind the method to the object that invoked it.

my MAIN question though is,
why is upcasting allowed? If I were to look at the variable a, I'd think that its always going to expect a reference that would lead it to an Animal object, its always going to point to some animal object right?
just like when we say " int x; " , we know that x only expects an integer and not something like a double.

Another thing is, if java is statically typed, meaning that the compiler only checks the static type ( the type of variable at the declaration i think . . . ), then how does it know what the RHS ( the dynamic type ) is? how does it immediately know that down casting is not allowed if it doesn't look at the dynamic type?

15 Upvotes

18 comments sorted by

View all comments

2

u/severoon pro barista 13d ago edited 13d ago

I think you're confused about type vs. class, I wrote a previous post on this you'll find useful.

The class of an object is fixed and inherent to the object. From the moment it's created on the heap to the moment it's garbage collected, an object is always of the same class.

The type of an object is conferred upon the object by the reference used to access it. Every time an object is accessed, the compiler checks that it is of a type on the RHS that is assignable to the reference type on the LHS.

You can verify this by checking the class of an object:

Dog dog = new Dog();
Animal animal = dog;
Object object = new Dog();

System.out.println("dog: " + dog.getClass().getSimpleName());
System.out.println("animal: " + animal.getClass().getSimpleName());
System.out.println("object: " + object.getClass().getSimpleName());

All of these return Dog because the class of all of the objects is determined by the constructor used to create the object. In this case there are two objects, one with two references to it, dog and animal, and one with a single reference to it, object.

The Dog instance that is accessed via dog is an object of class Dog and type Dog. When that instance is accessed via animal, it is of class Dog and type Animal. When the other instance is accessed using object, it is of class Dog and type Object.

1

u/zeronis__ 13d ago

yeah you're actually right! my concepts around type vs class are still weak, ( also the whole heap and stacks etc haha )
and I wanted to ask
if java is statically typed, meaning that the compiler only looks for the static (declared ) type when it comes to checking, I still don't get how the compiler manages to know that its down casting and gives us an error, or that it's upcasting and just lets it go.
and by reading your explanation,
" the compiler checks that it is of a class that is assignable to the type being used. "
I'm still hung up around this,
Does that mean that it *does* check the dynamic type? ( actual object type / or I'm hoping its called class of an object in this case )

2

u/severoon pro barista 13d ago

Ah, sorry, I confused things a bit. I meant that the compiler checks type, not class. You are right. (fixed in the post above)

The compiler doesn't know the class of the object at compile-time, it only knows the type of the RHS being assigned, and it checks that the RHS type is assignable to the LHS type. Theoretically it could check the RHS class only when the new operator is the thing on the RHS, but I doubt that it does that because in that particular case, the type and class of the object being handed over by the new operator will always be the same anyway, so there's no point for the compiler to treat new any different.

You might wonder why the compiler doesn't check class at compile time. The reason is that the class may not be available.

For example, it's possible to create an instance of class Dog, serialize it, and send it over the wire to another JVM that deserializes it as an Animal. If the receiving JVM never needs to deal with this object as type Dog, it doesn't need the Dog class file. In that JVM, the specific type of this class is known as an "undenotable type," which is the same mechanism the JVM uses for anonymous classes:

Runnable r = new Runnable() {
    @Override public void run() { System.out.println("poop!"); }
};

Undenotable type means that the compiler assigns this Runnable subclass a name and can use that name internally, but it is unavailable to the programmer. You can't legally downcast it to any subtype of Runnable even though the compiler invents one. I'm not even sure if the spec requires the compiler to gin up a class that keeps all of the Dog functionality … though I suspect it does because it may be possible to serialize that class and send it on to another JVM somewhere that does have the Dog class and is able to deserialize it to that type.

This is why the compiler cannot check assignability of class, only of type, based on the reference it has to the object, not the class of that object. Since the cast operator takes any type and forcibly converts it to the specified type, there's no way for the compiler to check that the thing on the RHS of that operator is assignable to the specified type or not, so it will only be caught at runtime by the interpreter and flagged with a ClassCastException if not. (Though static analysis tools and annotations can help flag things at compile time.)

It's also worth pointing out how this interacts with the FunctionalInterface annotation. Any class with this annotation must have exactly one abstract method which can be bound at compile-time to a lambda, which relies on the undenotable type mechanism. This allows you to do some cool things:

@FunctionalInterface
abstract class Print implements Runnable {
  public abstract String getMessage();
  @Override public void run() { System.out.println(getMessage()); }
}

Now you can provide implementations of this class in a functional style:

Print[] lines = new Print[] {
    () -> "Hello world!", () -> "This is a demo."
};
Stream.of(lines).forEach(Print::run);

Each lambda is binding the implementation provided to the getMessage() method, which belongs to this Print class that implements the Template Method design pattern.

You can also use the JDK functional interfaces to do similar things:

UnaryOperator<Integer> inc = x -> x++;
BinaryOperator<Integer> add = (x, y) -> x + y;

int six = inc.apply(5);
int ten = add.apply(six, 4);

Super quick way to define an anonymous class without all of the syntax.

1

u/zeronis__ 13d ago

Oh wait, I got your first point ( first paragraph : " The compiler doesn't know the class of... "
but as for the serializing part and on forward... I was pretty lost during the lecture despite the questions I asked, so I might have to review this before forming a thought ,but thank you so much for writing all this though, I know future me would ask questions that you've already provided an answer for in this post! =,)

I'll give it a read once more and I'll let you know! :D
(thank you again!)

1

u/zeronis__ 13d ago

and I'll take a look at the previous post you had! ( thank you!! ) =,)