Disclaimer: I'm the creator of EqualsVerifier. I only just discovered this question :).
The workaround Joachim Sauer mentions is correct.
Let me explain why EqualsVerifier does not like your implementation. Let's pretend for now that Person
is not abstract; it makes the examples a bit simpler. Let's say we have two Person
objects, like this:
Person person1 = new Person("John");
Person person2 = new Worker("John", "CEO of the world");
And let's call equals
on both these objects:
boolean b1 = person1.equals(person2); // returns true
boolean b2 = person2.equals(person1); // returns false
b1
is true, because Person
's equals
method is called, and it ignores workDescription
. b2
is false, because Worker
's equals
method is called, and the instanceof
or getClass()
check in that method returns false.
In other words, your equals
method is no longer symmetric, and this is a requirement for a correct implementation of equals
, according to the Javadoc.
You can indeed use getClass()
to get around this problem, but then you run into another problem. Let's say you use Hibernate, or a mocking framework. These frameworks use bytecode manipulation to create subclasses of your class. Essentially, you'll get a class like this:
class Person$Proxy extends Person { }
So let's say you make a round trip to the database, like this:
Person person1 = new Person("John");
em.persist(person1);
// ...
Person fetchedPerson = em.find(Person.class, "John");
And now let's call equals
:
boolean b3 = person1.equals(fetchedPerson); // returns false
boolean b4 = fetchedPerson.equals(person1); // also returns false
b3
and b4
are false because person1
and fetchedPerson
are of different classes (Person
and Person$Proxy
, to be precise). equals
is symmetric now, so at least it follows the contract, but it's still not what you want: fetchedPerson
doesn't "behave" like a Person
anymore. In technical terms: this breaks the Liskov Substitution Principle, which is the basis for Object-Oriented Programming.
There is a way to make all this work, but it's quite complicated. (If you really want to know: this article explains how.) To keep things simple, EqualsVerifier suggests that you make your equals
and hashCode
methods final. In most cases, this will work fine. If you really need to, you can always take the complicated route.
In your case, since Person
is abstract, you could also choose to not implement equals
in Person
, but only in Worker
(and any other subclasses you may have).