public class Animal {
public void eat() {}
}
public class Dog extends Animal {
public void eat() {}
public void main(String[] args) {
Animal animal = new Animal();
Dog dog = (Dog) animal;
}
}
The assignment Dog dog = (Dog) animal;
does not generate a compilation error, but at runtime it generates a ClassCastException
. Why can't the compiler detect this error?
The code generates a compilation error because your instance type is an Animal:
Downcasting is not allowed in Java for several reasons. See here for details.
By using a cast you're essentially telling the compiler "trust me. I'm a professional, I know what I'm doing and I know that although you can't guarantee it, I'm telling you that this
animal
variable is definitely going to be a dog."Since the animal isn't actually a dog (it's an animal, you could do
Animal animal = new Dog();
and it'd be a dog) the VM throws an exception at runtime because you've violated that trust (you told the compiler everything would be ok and it's not!)The compiler is a bit smarter than just blindly accepting everything, if you try and cast objects in different inheritence hierarchies (cast a Dog to a String for example) then the compiler will throw it back at you because it knows that could never possibly work.
Because you're essentially just stopping the compiler from complaining, every time you cast it's important to check that you won't cause a
ClassCastException
by usinginstanceof
in an if statement (or something to that effect.)To develop the answer of @Caumons:
Now take a look at this solution.
Now we test out application:
And here is the result :
Now you can easily add new fields to father class without being worried of your children codes to break;
Because theoretically
Animal animal
can be a dog:Generally, downcasting is not a good idea. You should avoid it. If you use it, you better include a check:
Dog d = (Dog)Animal; //Compiles but fails at runtime
Here you are saying to the compiler "Trust me. I know
d
is really referring to aDog
object" although it's not. Remember compiler is forced to trust us when we do a downcast.So when the JVM at the runtime figures out that the
Dog d
is actually referring to anAnimal
and not aDog
object it says. Hey... you lied to the compiler and throws a big fatClassCastException
.So if you are downcasting you should use
instanceof
test to avoid screwing up.if (animal instanceof Dog) { Dog dog = (Dog) animal; }
Now a question comes to our mind. Why the hell compiler is allowing the downcast when eventually it is going to throw a
java.lang.ClassCastException
?The answer is that all the compiler can do is verify that the two types are in the same inheritance tree, so depending on whatever code might have come before the downcast, it's possible that
animal
is of typedog
.Consider the following code snipet:
However, if the compiler is sure that the cast would not possible work, compilation will fail. I.E. If you try to cast objects in different inheritance hierarchies
String s = (String)d; // ERROR : cannot cast for Dog to String
Dog d = new Dog(); Animal animal1 = d; // Works fine with no explicit cast Animal animal2 = (Animal) d; // Works fine with n explicit cast
Both of the above upcast will work fine without any exception because a Dog IS-A Animal, anithing an Animal can do, a dog can do. But it's not true vica-versa.
In order to avoid this kind of ClassCastException, if you have:
You can define a constructor in B that takes an object of A. This way we can do the "cast" e.g.: