Subclass: object is not equal to an instance of a trivial subclass with equal fields

Subclass: object is not equal to an instance of a trivial subclass with equal fields: ...
Consider making the class final.

This error occurs when a class is non-final, and an instance of this class is not equal to an instance of a (dynamically generated) subclass of this class, even though their fields are all equal. It is easy to break the equals contract when inheritance is involved, even accidentally, and EqualsVerifier will always err on the side of caution.

There are several ways to fix this problem:

Make the class final

Make the class final. This way, all inheritance headaches will be avoided. However, this is not always possible.

Use instanceof

In frameworks like Java EE and Hibernate, it’s often not allowed to make certain classes final, even if you don’t intend to subclass them. In these cases, it’s probably a safe bet that the framework will create dynamic proxy subclasses for you class. This error will most likely only pop up if you use getClass() in your equals method, like so:

public boolean equals(Object other) {
    if (other == null || !getClass().equals(other.getClass())) {
        return false;
    }
    // ...
}

The solution, then, is to use an instanceof test instead:

public boolean equals(Object other) {
    if (!(other instanceof Foo)) {
        return false;
    }
    // ...
}

This way, a dynamic proxy subclass generated by the framework can still be equal to an object that you instantiate directly.

Unfortunately, with Hibernate it’s not possible to make the equals and hashCode methods final as well. This means it’s still possible to accidentally (or purposely) break the contract by making a subclass and overriding equals and hashCode.

Make equals and hashCode final

If you do intend your class to be overridden, but subclasses won’t add state that needs to be included in the equals/hashCode contract, the above advice applies as well. Again, make you equals and hashCode methods final, to signal that no such state is to be added in subclasses.

Use canEqual

If you intend your class to be overridden, and you also want subclasses to add state that needs to be included in the contract, things get complicated. In Item 8 of Effective Java, Josh Bloch argues that it is impossible to achieve this without breaking the contract. Nevertheless, it turns out to be possible, by introducing a new method called canEqual. This article by Martin Odersky, Bill Venners and Lex Spoon explains how to achieve this. If you decide to go down this path, you will need to supply EqualsVerifier with an example of a subclass with added state, like this:

EqualsVerifier.forClass(Foo.class)
    .withRedefinedSubclass(SomeSubclass.class)
    .verify();

Obligatory RTFM comment

You can read more about handling inheritance in the manual.

Updated: