Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Edited by NavBot (vkp) v1.0

Wiki Markup
According to the Java API \[[API 2006|AA. Java References#API 06]\] class {{java.lang.Object}} documentation:

If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result.

Failure to follow this contract is a common source of bugs. Notably, immutable objects are exempt because they need not override the hashcode() method.

Noncompliant Code Example

Even when the equals() method conveys logical equivalence between classes, the hashCode() method returns distinct numbers as opposed to returning the same values. Its contract requires it to return the same values for equal objects. This noncompliant code example stores a credit card number into a HashMap and retrieves it. The expected retrieved value is Java, however, null is returned instead. The reason for this erroneous behavior is that the hashCode() method is not overridden which means that a different bucket would be looked into than the one used to store the original value.

Code Block
bgColor#FFCCCC
public final class CreditCard {
  private final int number;

  public CreditCard(int number) {
    this.number = (short) number;
  }

  public boolean equals(Object o) {
    if (o == this) {
      return true;
    } 
    if (!(o instanceof CreditCard)) {
      return false;
    }
    CreditCard cc = (CreditCard)o;
    return cc.number == number; 
  }

  public static void main(String[] args) {
    Map<CreditCard, String> m = new HashMap<CreditCard, String>();
    m.put(new CreditCard(100), "Java");
    // Assuming Integer.MAX_VALUE is the largest number for card
    System.out.println(m.get(new CreditCard(100)));  
  }
}

Compliant Solution

Wiki Markup
This compliant solution shows how the {{hashCode()}} method can be overridden so that the same value is generated for any two instances that compare equal when {{Object.equals()}} is used. Bloch discusses the recipe to generate such a hash function in good detail \[[Bloch 2008|AA. Java References#Bloch 08]\].

Code Block
bgColor#ccccff
import java.util.Map;
import java.util.HashMap;

public final class CreditCard {
  private final int number;
  
  public CreditCard(int number) {
    this.number = (short) number;
  }

  public boolean equals(Object o) {
    if (o == this) {
      return true;
    } 
    if (!(o instanceof CreditCard)) {
      return false;
    }
    CreditCard cc = (CreditCard)o;
    return cc.number == number; 
  }

  public int hashCode() {
    int result = 7;
    result = 37 * result + number;
    return result;
  }

  public static void main(String[] args) {
    Map<CreditCard, String> m = new HashMap<CreditCard, String>();
    m.put(new CreditCard(100), "Java");
    System.out.println(m.get(new CreditCard(100)));
  }
}

Risk Assessment

Overriding the equals() method without overriding the hashCode() method can lead to unexpected results.

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

MET13- J

low

unlikely

high

P1

L3

Automated Detection

TODO

Related Vulnerabilities

Search for vulnerabilities resulting from the violation of this rule on the CERT website.

References

Wiki Markup
\[[API 2006|AA. Java References#API 06]\] [Class Object|http://java.sun.com/javase/6/docs/api/java/lang/Object.html]
\[[Bloch 2008|AA. Java References#Bloch 08]\] Item 9: Always override {{hashCode}} when you override {{equals}}
\[[MITRE 2009|AA. Java References#MITRE 09]\] [CWE ID 581|http://cwe.mitre.org/data/definitions/581.html] "Object Model Violation: Just One of Equals and Hashcode Defined"


MET12-J. Follow the general contract while overriding the equals method      16. Methods (MET)      MET14-J. Follow the general contract when implementing the compareTo method