Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Do not violate any of these five conditions while overriding the equals method. Mistakes resulting from a violation of to the first condition are infrequent; it is consequently omitted from this discussion. The second and third conditions are highlighted. The rule for consistency implies that mutable objects may not satisfy the equals contract. It is good practice to avoid defining equals() implementations that use unreliable data sources such as IP addresses and caches. The final condition about the comparison with null is typically violated when the equals() code throws an exception instead of returning false. Because this does not constitute a security vulnerability, it is beyond the scope of this discussion.

...

This noncompliant code example violates transitivity though it satisfies the symmetry condition. In the first print statement, the comparison between p1 and p2 returns true, in the second, the comparison between p2 and p3 also returns false and true but in the third, the comparison between p1 and p3 returns false. This is counter intuitive as p1 and p2 must compare equal when they are both equal to a common objectcontradicts the transitivity rule.

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

  public Card(int number) {
    this.number = number;
  }

  public boolean equals(Object o) {
    if (!(o instanceof Card))
      return false;
    Card c = (Card)o;
    return c.number == number;
  }
}

class XCard extends Card {
  private String type;
  public XCard(int number, String type) {
    super(number);
    this.type = type;
  }

  public boolean equals(Object o) {
  if (!(o instanceof Card))
    return false;
    //normal Card, do not compare type 
    if (!(o instanceof XCard))
      return o.equals(this);
    //It is an XCard, compare type as well
    XCard xc = (XCard)o;
    return super.equals(o) && xc.type == type;
  }	  
  
  public static void main(String[] args) {
    XCard p1 = new XCard(1, "type1"); 
    Card p2 = new Card(1);
    XCard p3 = new XCard(1, "type2");
    System.out.println(p1.equals(p2)); //returns true
    System.out.println(p2.equals(p3)); //returns true
    System.out.println(p1.equals(p3)); //returns false, violating transitivity
  }
}

...

Wiki Markup
It is currently not possible to extend an instantiable class (as opposed to an {{abstract}} class) and add a value or field in the subclass while preserving the {{equals}} contract. This implies that composition must be preferred over inheritance. This technique does qualify as a reasonable workaround \[[Bloch 08|AA. Java References#Bloch 08]\]. It can be implemented by giving the {{XCard}} class a private {{card}} field and providing a a {{public}} {{viewCard}} method. 

...