Do you know Java: Beware of instanceof in equals


One of the most important methods is equals. So, it is also important how it is implemented.

What is the real issue with instanceof and equals? Let us see an implementation of it. First, let us introduce a simple class and its inheritant that demonstrate the usage of equals`.

public class Mug {
    private double capacity;

    // ctor, setter, getter

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null) {
            return false;
        }
        
        if (!(o instanceof Mug)) {
            return false;
        }
        
        Mug other = (Mug) o;
        return other.capacity == this.capacity;
    }
}

Then we see as:

Mug m1 = new Mug(1.2);
Mug m2 = new Mug(1.2);

System.out.println(m1.equals(m2)); // true
System.out.println(m2.equals(m1)); // true

What if there is an inheritant of Mug? How does equals work in that case?

public class PlasticMug extends Mug {
    private String plastic;

    // ctor, setters, getters

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null) {
            return false;
        }

        if (!(o instanceof PlasticMug)) {
            return false;
        }

        PlasticMug other = (PlasticMug) o;
        return other.getCapacity() == this.getCapacity() && other.plastic.equals(plastic);
    }
}

As seen above, let us apply the equals on the instances of Mug and PlasticMug.

System.out.println(mug.equals(plastic)); // true
System.out.println(plastic.equals(mug)); // false

instanceof operator returns true if the type of the object is the same, or inherited from the type on right. On the other side, instanceof never returns true if the right operand is one of the parents of the type of the object sitting on the left side of instanceof.

How can we fix it?

getClass() method returns the exact class literal of the object whether its static type is Object.

The equals method of Mug. PlasticMug is similar.

@Override
public boolean equals(Object o) {
    if (this == o) {
        return true;
    }
    if (o == null) {
        return false;
    }
 
    if (getClass() != o.getClass()) {
        return false;
    }
    
    Mug other = (Mug) o;
    return other.capacity == this.capacity;
}

After changing the two implementations of equals, then the results look as follows:

System.out.println(mug.equals(plastic)); // false 
System.out.println(plastic.equals(mug)); // false

As previously said, beware of using instanceof in equals, and use getClass() method instead.

Code can be found: https://github.com/torokmark/do-you-know-java