Question

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.
Was it helpful?

Solution 2

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.

OTHER TIPS

Might be easy to think about it as animals. Lets say the grandparent class is Animal, the parent class is Dog, and the child class is GermanShepherd.

Your first example would be as such:

Dog dog = new Dog();
System.out.println((Animal)dog);  
//casting GermanShepherd throws compiler error

A dog can always be an animal, but a dog might not always be a german shepherd (it might be a poodle).

Your second example:

Animal animal = new GermanShepherd(); 
//Switching Animal with GermanShepherd throws run time error.

Similarly, a GermanShepherd will always be an animal, but an animal might not be a GermanShepherd (could be a turtle).

ParentClass has the method getWidget; ChildClass has the inherited method getWidget and declared a new method getAnotherWidget. This means that when the compiler sees that you have a ChildClass object, it knows that ChildClass can do everything that ParentClass can do, therefore it's safe to treat ChildClass as a ParentClass. However, when the compiler sees a ParentClass, it doesn't know that ParentClass has a getAnotherWidget method - you can determine this at runtime (via e.g. instanceof ChildClass), or at compile time you can tell the compiler "this ParentClass object is really a ChildClass - trust me, I know what I'm doing" by performing a cast ChildClass childClass = (ChildClass)parentClass - if you're wrong then you'll get a ClassCastException. You need to perform this cast even if ChildClass merely inherited from ParentClass without adding any more methods - the compiler is not clever enough to know that the ChildClass is basically equivalent to a ParentClass.

Dynamic binding only works one way because the ParentClass does not know all of its children - you might load additional inheriting classes at runtime. In contrast, ChildClass knows who its ParentClass is, and so it can override ParentClass's methods. (This is why ChildClass can call super.getWidget() but ParentClass can't call child.getWidget() - even if ParentClass knew who all of its children were, how would it know which child class you were referring to? Rather than overburden the syntax, it's simpler for Java to require you to instantiate a ChildClass object if you want to use its methods rather than calling them through ParentClass)

Dynamic binding and casting go hand in hand. They way I understand it is ALWAYS read from right to left to figure out if you can bind and cast... e.g:

object A          ←       object B
                           is a

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top