Course – LS – All

Get started with Spring and Spring Boot, through the Learn Spring course:

>> CHECK OUT THE COURSE

1. Overview

instanceof is an operator that compares an object’s instance to a type. It’s also called a type comparison operator.
In this tutorial, we’ll look at different alternatives to the traditional instanceof approach. We may need alternatives to improve code design and readability.

2. Example Setup

Let’s develop a simple program, Dinosaur and Species. This program will have a parent class and two child classes, i.e., the child classes will extend the parent class.
First, let’s create the parent class:

public class Dinosaur {
}

Next, let’s create the first child class:

public class Anatotitan extends Dinosaur {
    String run() {
        return "running";
    }
}

Finally, let’s create the second child class:

public class Euraptor extends Dinosaur {	
    String flies() {
        return "flying";
    }
}

The Dinosaur class has other methods common to the subclasses, but we’re skipping them for simplicity.
Next, let’s write a method to create new instances of our objects and invoke their movement. We’ll use instanceof to check our new instance type before returning a result:

public static void moveDinosaur(Dinosaur dinosaur) {
    if (dinosaur instanceof Anatotitan) {
        Anatotitan anatotitan = (Anatotitan) dinosaur;
        anatotitan.run();
    } 
    else if (dinosaur instanceof Euraptor) {
        Euraptor euraptor = (Euraptor) dinosaur;
        euraptor.flies();
    }
}

In the next sections, we’ll apply different alternatives.

3. Using getClass()

The getClass() method helps to get the class of an object. We can use the getClass() as an alternative to instanceof when checking whether an object belongs to a particular class or not.
In our example setup, let’s maintain the structure of our parent class and the subclasses. Then, let’s write a test method for this approach. We’ll use getClass() instead of instanceof:

public static String moveDinosaurUsingGetClass(Dinosaur dinosaur) {
    if (dinosaur.getClass().equals(Anatotitan.class)) {
        Anatotitan anatotitan = (Anatotitan) dinosaur;
        return anatotitan.run();
    } else if (dinosaur.getClass().equals(Euraptor.class)) {
        Euraptor euraptor = (Euraptor) dinosaur;
        return euraptor.flies();
    }
    return "";
}

Let’s proceed to write a unit test for this approach:

@Test
public void givenADinosaurSpecie_whenUsingGetClass_thenGetMovementOfEuraptor() {
    assertEquals("flying", moveDinosaurUsingGetClass(new Euraptor()));
}

This alternative maintains our original domain objects. What changes is the use of getClass().

4. Using Polymorphism

The concept of polymorphism makes a subclass override a method from the parent class. We can use this concept to change our example setup and improve our code design and readability.
Since we know all dinosaur move, we can change our design by introducing a move() method in our parent class:

public class Dinosaur {	
    String move() {
        return "walking";
    } 
}

Next, let’s modify our subclasses by overriding the move() method:

public class Anatotitan extends Dinosaur {
    @Override
    String move() {
        return "running";
    }
}
public class Euraptor extends Dinosaur {
    @Override
    String move() {
        return "flying";
    }
}

Now we can reference the subclasses without using the instanceof approach. Let’s write a method that accepts our parent class as an argument. We’ll return our dinosaur movement based on its specie:

public static String moveDinosaurUsingPolymorphism(Dinosaur dinosaur) { 
    return dinosaur.move(); 
}

Let’s write a unit test for this approach:

@Test 
public void givenADinosaurSpecie_whenUsingPolymorphism_thenGetMovementOfAnatotitan() { 
    assertEquals("running", moveDinosaurUsingPolymorphism(new Anatotitan()));
}

When possible, it’s recommended to change our design itself using this approach. The use of instanceof is often an indication that our design violates the Liskov Substitution Principle (LSP).

5. Using an Enumeration

In enum types, variables can be defined as sets of predefined constants. We can use this approach to improve our simple program.
First, let’s create an enum with constants that have methods. The methods of the constants override an abstract method in the enum:

public enum DinosaurEnum {
    Anatotitan {
        @Override
        public String move() {
            return "running";
        }
    },
    Euraptor {
        @Override
        public String move() {
            return "flying";
        }
    };
    abstract String move();
}

The enum constants act like subclasses used in other alternatives.
Next, let’s modify our moveDinosaur() method to use the enum type:

public static String moveDinosaurUsingEnum(DinosaurEnum dinosaurEnum) {
    return dinosaurEnum.move();
}

Finally, let’s write a unit test for this approach:

@Test
public void givenADinosaurSpecie_whenUsingEnum_thenGetMovementOfEuraptor() {
    assertEquals("flying", moveDinosaurUsingEnum(DinosaurEnum.Euraptor));
}

This design made us eliminate the parent class and the subclasses. This approach is not recommended in a complex scenario where the parent class will have more behavior than our example setup.

6. Using Visitor Pattern

The Visitor pattern helps to operate on similar/related objects. It moves the logic from the object class to another class.
Let’s apply this approach to our example setup. First, let’s create an interface with a method and pass a Visitor as an argument. This will help to retrieve the type of our object:

public interface Dinosaur {
    String move(Visitor visitor);
}

Next, let’s create a Visitor interface with two methods. The methods accepts our subclass as an argument:

public interface Visitor {
    String visit(Anatotitan anatotitan);
    String visit(Euraptor euraptor);
}

Next, let’s make our subclasses implement the Dinosaur interface and override its method. The method has Visitor as an argument to retrieve our object type. This method replaces the use of instanceof:

public class Anatotitan implements Dinosaur {
    public String run() {
        return "running";
    }
    @Override
    public String move(Visitor dinoMove) {
        return dinoMove.visit(this);
    }
}

Next, let’s create a class to implement our Visitor interface and override the methods:

public class DinoVisitorImpl implements Visitor {
    @Override
    public String visit(Anatotitan anatotitan) {
        return anatotitan.run();
    }
    @Override
    public String visit(Euraptor euraptor) {
        return euraptor.flies();
    }
}

Finally, let’s write a test method for this approach:

public static String moveDinosaurUsingVisitorPattern(Dinosaur dinosaur) {
    Visitor visitor = new DinoVisitorImpl();
    return dinosaur.move(visitor);
}

Let’s write a unit test for this approach:

@Test
public void givenADinosaurSpecie_whenUsingVisitorPattern_thenGetMovementOfAnatotitan() {
    assertEquals("running", moveDinosaurUsingVisitorPattern(new Anatotitan()));
}

This approach made use of an interface. The Visitor contains our program logic.

7. Conclusion

In this article, we’ve examined different instanceof alternatives. The instanceof approach potentially violates the Liskov Substitution Principle. Adopting alternatives gave us a better and more solid design. The polymorphism approach is recommended as it adds more value.
As usual, the complete source code is available over on GitHub.

Course – LS – All

Get started with Spring and Spring Boot, through the Learn Spring course:

>> CHECK OUT THE COURSE
res – REST with Spring (eBook) (everywhere)
Comments are open for 30 days after publishing a post. For any issues past this date, use the Contact form on the site.