JavaObject-oriented programmingInheritance and polymorphism

Runtime type checking

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.
How did you like the theory?
Report a typo