1. Typecasting
Variables that store reference types (classes) can also be converted to different types. But this only works within a single type hierarchy. Let's look at a simple example. Suppose we have the following class hierarchy, in which the classes below inherit the classes above.
Typecasting of reference types as well as primitive ones is also categorized as either widening and narrowing.
We see that the Cat class inherits the Pet class, and the Pet class, in turn, inherits the Animal class.
If we write code like this:
Animal kitten = new Cat();
This is a widening type conversion. It is also called an implicit cast. We've widened the cat reference so that it now refers to a Cat object. With a type conversion like this, we will not be able to use the kitten reference to call methods that are present in the Cat class but absent in the Animal class.
A narrowing conversion (or explicit cast) happens in the opposite direction:
Cat cat = (Cat) kitten;
We explicitly indicated that we want to cast the reference stored in the kitten variable (whose type is Animal) to the Cat type.
2. Checking the type of an object
But you need to be very careful here. If you do this:
Animal beast = new Cat();
Wolf grayWolf = (Wolf) beast;
The compiler will allow this code, but there will be an error when the program runs! The JVM will throw an exception:
Exception in thread "main" java.lang.ClassCastException: Cat cannot be cast to a Wolf
References to a Cat object can only be stored in variables whose type is an ancestor of the Cat class: Pet, Animal, or Object.
Why is that?
The relevant point here is that an object reference is used to refer to the methods and variables of that object. And there won't be any problems if we use an Animal variable to store a reference to a Cat object: the Cat type always has a variables and methods of the Animal type — it inherited them!
But if the JVM allowed us to store a reference to a Cat object in a Wolf variable, then we might have a situation where we might try to use the grayWolf variable to call a method that does not exist in the Cat object stored in that variable. That's why this arrangement is not allowed.
Java has a special instanceof
operator that lets you check if an object is of a certain type and therefore can be stored into a variable of a certain type. It looks quite simple:
variable instanceof Type
Example:
Animal beast = new Cat();
if (beast instanceof Wolf)
{
Wolf grayWolf = (Wolf) beast;
}
This code won't cause errors — even at runtime.
Here are some more examples that illustrate the situation:
Widening type conversion | Description |
---|---|
|
This is a classic widening conversion — no type conversion operator is required. Now only the methods defined in the On the |
Narrowing type conversion | |
|
Classic narrowing conversion: You need to add a type check and a cast operator.
The Cow cow variable stores a reference to a Whale object.
We verify that this is the case, and then perform a (narrowing) type conversion. Or as it is also called: a type cast
.
|
|
You can narrow a reference type without checking the type of the object.
If the cow variable refers to an object that is not a Whale , then an InvalidClassCastException will be generated.
|
3. Calling the original method: the super
keyword
When overriding a parent class's method, sometimes rather than replacing it with our own, we only want to supplement it slightly.
It would be cool if we could the parent class's method in our method, and then execute some of our own code. Or perhaps first execute our own code, and then call the parent class's method.
And Java lets us to just that. To call a method of the parent class, do this:
super.method(arguments);
Examples:
class PeaceTime
{
public double getPi()
{
return 3.14;
}
}
class WarTime extends PeaceTime
{
public double getPi()
{
return super.getPi()*2; // 3.14*2
}
}
In wartime, the value of Pi
can be greater than 6! Of course, we're joking, but this example demonstrates how this all can work.
Here are a couple more examples to clarify things a bit:
Code | Description |
---|---|
|
Cow and Whale classes
|
|
The screen output will be:
|
This is hard stuff. Honestly, it's one of the hardest things in OOP. That said, you do need to know and understand it.
GO TO FULL VERSION