Checker EqualsHashCode   as of Julia version 2.6.0 (built on 6 Sep 2018)

belongs to group Basic

Identify incorrect definitions of equals()/hashCode()


Redefinitions of the equals()/hashCode() methods from java.lang.Object must be consistent in the sense that, for instance, if two objects are equals() then hashCode() must yield the same value on both. The validity of such a property is in general undecidable, but most incorrect definitions amount to simple cases, where one of the two methods is missing. However, it is often correct to redefine only one of those methods and Julia is aware of many such situations.

Inconsistent definitions of equals()/hashCode() induce unexpected behaviors when objects are put inside most collection classes of the standard Java library.

Action: make equals() consistent with hashCode(). In many cases, this just amounts to provide the missing definition for one of them.

Examples


Consider the following classes:

public class EqualsHashCode {
  private final String name;

  public EqualsHashCode(String name) {
    this.name = name;
  }

  @Override
  public boolean equals(Object other) {
    return other instanceof EqualsHashCode && ((EqualsHashCode) other).name.equals(name);
  }
}
class B {
  private final String name;

  public B(String name) {
    this.name = name;
  }

  @Override
  public int hashCode() {
    return name.hashCode();
  }

  @Override
  public boolean equals(Object other) {
    return other instanceof B && ((B) other).name.equals(name);
  }
}

and

class C extends B {
  private final int age;

  public C(String name, int age) {
    super(name);

    this.age = age;
  }

  @Override
  public boolean equals(Object other) {
    return super.equals(other) && other instanceof C && ((C) other).age == age;
  }
}

This checker issues the following warning:

EqualsHashCode.java:10: [EqualsHashCode: NoHashCodeWarning] equals(Object) defined here, but no hashCode()

since the missing hashCode() allows one to create two instances of EqualsHashCode that are equals() but have distinct hashCode(). Note that there is no warning related to the missing definition of hashCode() inside class C, since the definition of equals() inside C requires equality wrt the superclass B and hence entails the identity of the hashcodes; then, it is correct to miss that definition of hashCode().

In this example, the programmer should add the following definition (or similar) to class C:

@Override
public int hashCode() {
  return super.hashCode() ^ age;
}

Adding fields while redefining classes

If a class defines its equals() method and a subclass adds an instance field without redefining equals(), this checker will issue a warning since the extra field should likely be included in the test of equals(). That might however not be the case, if for instance the value of the extra field is the same when two objects are equals(). In that case, it is possible to annotate the extra field with @IrrelevantForEquals, so that this checker will not issue any more warning for that situation.