TegDMK stands for “Things Every Great Developer Must Know”, and will be a series of articles discussing essential, but often overlooked stuff.

So for the first one… During doing interviews with Java developers and facilitating Global Day of Code Retreat in Cluj, I have realized how much confusion there is about equals() and hashCode() methods in Java. We’ll focus on equals() in this article.

In my opinion, no one should call themselves a Java developer (much less a senior Java developer), if they don’t understand this topic perfectly. Seriously. And don’t tell me that you have an IDE that will generate the equals() method for you. It might, but you must still understand what you are doing.

A must read

Start with javadoc for Object.equals(), which should get you started. There are many other great articles online, just google it.

Let’s demonstrate some real-world examples of mistakes in equals() implementation now.

Not checking the reference

This one is not really an error, but it is a good practise to compare yourself with the other object using o == this as the very first thing in your equals() method:

public boolean equals(Object o) {
    if (o == this) return true; //this is GOOD
        //...
    }
}

Not checking the object before casting

This code will result in a java.lang.ClassCastException if someone tries to compare your object with String for example:

public boolean equals(Object o) {
    ExpectedClass p = (ExpectedClass)o; //this is WRONG
    //...
}

Creating an infinite recursion

Hard to decode what the author meant here, but the following code will definitely result in an infinite recursion and a java.lang.StackOverflowError eventually:

public boolean equals(Object o) {
    if (o instanceof ExpectedClass) {
        return this.equals(o);  //this is WRONG
    }
}

Not checking for null

If you don’t check for o != null, do not be surprised that your code crashes with java.lang.NullPointerException sooner or later. This is in addition to missing check before casting problem here:

public boolean equals(Object o) {
    ExpectedClass p = (ExpectedClass) o;
    return someField.equals(p.someField) &&     //this is WRONG
           anotherField.equals(p.anotherField); //this is WRONG
}

Not using equals() for fields

If you need to compare equality of fields in your equals() (most of the time, this is exactly what you want to do), you’d better use equals(), not ==. This is not only true for String but also for any other object types. There might be like 0.1% chance that your case is special and == is OK, but not normally:

public boolean equals(Object o) {
    //...
    if (someField != p.someField) return false; //this is most likely WRONG
    //...
}

Using hashCode() inside equals()

The following code might work most of the time, and that is exactly why it is so dangerous. One must never rely on hashCode() when implementing equals(). And every Java developer should be able to explain why – so we leave this one as an example to the reader ;)

public boolean equals(Object o) {
    if (o == null) return false;
    if (!(o instanceof ExpectedClass)) return false;
    ExpectedClass o2 = (ExpectedClass) o;
    return hashCode() ==  o2.hashCode();  //this is WRONG
}