Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Rules for Overriding the equals Method in Java

Tech May 10 3

Implementing a custom equals method is a frequent source of latent bugs. In many cases, the healthiest choice is to leave the method untouched so that identity alone determines equality. This default behavior is correct when:

  • Instances are unique by nature. Classes modeling active entities rather than values, such as Thread, fit this pattern. The identity-based semantics provided by Object.equals accurately capture their behavior.
  • Logical comparison offers no value. The designers of java.util.regex.Pattern, for instance, chose not to implement regular-expression equality because client code rarely needs it. Retaining the inherited implementation avoids unnecessary complexity.
  • A superclass already satisfies the requirement. Concrete collection implementations typically inherit appropriate logic from abstracts like AbstractSet, AbstractList, or AbstractMap.
  • The class is inaccessible and never compared. For private or package-private types guaranteed to avoid equality checks, overriding equals solely to trap accidental invocations remains an option:
@Override
public boolean equals(Object obj) {
    throw new AssertionError("Should never be called");
}

Override equals only when a class conceptually represents a value distinct from its object identity, and no ancestor has already introduced value-based comparison. Types such as Integer and String exemplify value classes: programmers expect equals to reveal whether two references denote the same logical value, not whether they point to the same memory location. Satisfying this expectation also allows enstances to function correctly as keys in hash maps or elements in sets.

Consider a simple class without a custom implementation:

class State {
    String value;
}

State first = new State();
State second = new State();

System.out.println(first == second);      // false
System.out.println(first.equals(second)); // false

After overriding equals to compare the meaningful field, logically equivalent instances evaluate as equal even when they are distinct objects:

class State {
    String value;

    @Override
    public boolean equals(Object obj) {
        if (obj == this) return true;
        if (!(obj instanceof State)) return false;
        State other = (State) obj;
        return java.util.Objects.equals(value, other.value);
    }
}

State first = new State();
State second = new State();

System.out.println(first == second);      // false
System.out.println(first.equals(second)); // true

Some value classes do not require a custom implementation. Classes employing instance control—guaranteeing at most one object per value—or enum types merge logical equality with identity. For them, Object.equals already behaves correctly.

If you do override equals, you must honor its general contract. The specification mandates that the method implement an equivalence relation with the following properties:

  • Reflexive: For any non-null reference x, x.equals(x) returns true.
  • Symmetric: For non-null references x and y, x.equals(y) yields true if and only if y.equals(x) does.
  • Transitive: For non-null references x, y, and z, if x.equals(y) and y.equals(z) are true, then x.equals(z) must also be true.
  • Consistent: Provided no field participating in the comparison changes, repeated calls to x.equals(y) return the same boolean result.
  • Null rejection: For any non-null reference x, x.equals(null) returns false.

These constraints partition objects into equivalence classes whose members must be interchangeable from the perspective of any client. Violating any property can cause collections and other dependent classes to behave inconsistently or crash. Because objects routinely cross componant boundaries, defective equals implementations propagate bugs across modules, making strict compliance critical.

Tags: Java

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.