Runtime type checking
A variable of a base class can always refer to a subclass object. We can determine the actual type of the referred object at runtime.
Java provides several ways to do it:
- use the
instanceof
operator that can be used to test if an object is of a specified type; - use java reflection to obtain an object representing the class.
Let's consider these ways to check types of objects at runtime.
Here is a class hierarchy which we will use in samples:
class Shape {...}
class Circle extends Shape {...}
class Rectangle extends Shape {...}
The hierarchy is very simple, we don't show fields and methods of the classes. But it demonstrates the relation "IS-A" pretty well.
The keyword instanceof
The binary operator
instanceof
returns true
if an object is an instance of a particular class or its subclass.The base syntax:
obj instanceOf Class
Example. There are several instances of the classes above.
Shape circle = new Circle(); // the reference is Shape, the object is Circle
Shape rect = new Rectangle(); // the reference is Shape, the object is Rectangle
Let's determine their types:
boolean circleIsCircle = circle instanceof Circle; // true
boolean circleIsRectangle = circle instanceof Rectangle; // false
boolean circleIsShape = circle instanceof Shape; // true
boolean rectIsRectangle = rect instanceof Rectangle; // true
boolean rectIsCircle = rect instanceof Circle; // false
boolean rectIsShape = rect instanceof Shape; // true
So, the
instanceof
operator allows you to determine the actual type of an object that is referred by its superclass references.As you can see, it considers a subclass object as of superclass:
boolean circleIsShape = circle instanceof Shape; // true
Important, the type of an considered object must be a subtype (or the same type) of the specified class. Otherwise, it cannot be compiled.
Here is a non-compiled example:
Circle c = new Circle();
boolean circleIsRect = c instanceof Rectangle; // Inconvertible types
The second line contains the compile-time error: Inconvertible types.
Use reflection
1) Each object has the method
getClass
to obtain an object representing the class. We can directly compare the classes represented by objects at runtime using java reflection.Example. Here is an instance of
Circle
.Shape circle = new Circle();
Let's test it using reflection:
boolean equalsCircle = circle.getClass() == Circle.class; // true
boolean equalsShape = circle.getClass() == Shape.class; // false
boolean rectangle = circle.getClass() == Rectangle.class; // false
Unlike the
instanceof
operator, this approach performs the strict type testing and does not consider subclass object as of superclass.2) An object representing a class has the method
isInstance
that is similar to the instanceof
keyword.Example. Let's test
circle
again.boolean isIntanceOfCircle = Circle.class.isInstance(circle); // true
boolean isInstanceOfShape = Shape.class.isInstance(circle); // true
boolean isInstanceOfRectangle = Rectangle.class.isInstance(circle); // false
Like the
instanceof
operator, this method considers a subclass object as an instance of its superclass. But unlike the operator, the following example is successfully compiled:Circle c = new Circle();
boolean circleIsRect = Rectangle.class.isInstance(c); // false
When to use it
If you cast a superclass object to its subclass, it may throw
ClassCastException
if the object has another type. Before casting, you can check the actual type using one of the considered approaches.The following example demonstrates it.
Shape shape = new Circle();
if (shape.getClass() == Circle.class) {
Circle circle = (Circle) shape;
// now we can process it as a circle
}
Keep in mind, if a program has a lot of runtime type checks, it may indicate a poor design. Use runtime polymorphism to reduce them.