Let's say I have a GrandParentClass that gets inherited by ParentClass which gets inherited by ChildClass.
Why can I only cast "up the chain" but not down?
Why can I only dynamic bind "down the chain" but not up?
I'm looking for some reasoning I can apply to these questions instead of just memorizing the pattern. I'm really hoping this isn't just a "that's just the way it is" kind of answer.
Example of casting:
ParentClass object = new ParentClass();
System.out.println((GrandParentClass)object); //casting ChildClass throws compiler error
Example of dynamic binding:
GrandParentClass = new ChildClass(); //Switching GrandParentClass with ChildClass throws run time error.
The general reasoning for these casting rules (not only in Java) is that inheritance allows you build trees of classes, not just lists. To put it differently, not every object of type GrandParentClass
is necessarily an object of type ParentClass
and not every object of type ParentClass
is necessarily of type ChildClass
. You could conceive an alternative ParentClass2
inheriting from GrandParentClass
or simply instantiate an object of type GrandParentClass
. Imagine what would happen if you were allowed to access a GrandParentClass
object as if it was a ChildClass
object with all the methods and variables that are defined only in ChildClass
but not above. There is no meaningful resolution.
So, casting "up the chain" always makes sense, since the inheritance relation is known at compile-time. Your dynamic binding example is the same thing; You are binding "up the chain" (not at all "down the chain") since you are assigning a derived type object to a reference of a type it was derived from.