The reason why this is confusing on the face of it is that it is not something that you would typically do in real code, except in the case of a Factory.
As hinted at in Zabuza's comment, you can do this because a Cat
'is-a' kind of Animal
and so you can assign an object of type Cat
to an object of type Animal
. But you can't do the assignment the other way of course, because an Animal
is not a kind of Cat
.
Now, there are some lurking issues that come with actually being able to create an instance of the the supertype as well as the subtype that mean you typically wouldn't actually do this in real code because it complicates a lot of things down the road. What you would more likely do is make Animal
an interface and have a GenericAnimal
class that implements it, along with having Cat
implement it.
Say you have an object that represents a zoo, and most zoos typically have a collection of animals. The most obvious way to represent this would be this:
java.util.Collection<com.myproject.Animal> zooAnimals;
So now imagine the zoo builds a new habitat, and it's for a lion. For the sake of the story assume we have a very lazy data model and instead of having a specific com.myproject.animals.cats.Lion
subtype we just said "lions are cats, close enough". So to update the data structure that tracks all the animals and their names and addresses and favorite foods and whatever else, we might do this:
com.myproject.Animal newArrival = new com.myproject.animals.Cat("Larry the Lion", "Africa Exhibit", "Gazelles");
zooAnimals.add(newArrival);
Now imagine that the zoo continues to grow, and gets an Ostrich in the Africa habitat. And the same lazy data model applies so we just call it a Bird
.
com.myproject.Animal newArrival = new com.myproject.animals.Bird("Oliver the Ostrich", "Africa Exhibit", "Whatever Ostriches Eat");
zooAnimals.add(newArrival);
Now actually writing that exact code would normally only happen in very specific cases inside a factory object or something, and realistically type hierarchies like this have a tendency to not work very well in practice at all, contrary to what a lot of us learned in Object Oriented Programming class, but for the sake of the question that is an example situation where you might do what you are asking about.
Lastly, you also asked when you have to cast. You would have to do this if you had code that needed to know about any special methods or fields that the Cat
or Bird
types have that Animal
does not have. For instance the Cat
type might have a property called tailLength
because cats typically have tails and for whatever reason the zoo likes to keep track of that. Similarly the Bird
type might have a property called wingSpan
because birds have wings and we want to keep track of how big they are. The Animal type doesn't have any of these properties so if we get the object for the lion or the ostrich out of the zooAnimals collection (and maybe we looked at the name or something to figure out it was the lion) we would have to cast back to the Cat
type in order to access the tailLength
property. Same thing for the ostrich and it's wingspan.
for( Animal theAnimal : zooAnimals ){
if( theAnimal.getName().equals("Larry the Lion") ){
Cat theCat = (Cat)theAnimal;
System.out.println("Larry's tail is " + theCat.getTailLength() + " inches long";
}
else if( theAnimal.getName().equals("Oliver the Ostrich") ){
Bird theBird = (Bird)theAnimal;
System.out.println("Oliver's wingspan is " + theBird.getWingSpan() + " inches";
}
}
Again you probably wouldn't actually do something like that in real code, but perhaps it helps to illustrate the example.