Versions Compared

Key

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

Wiki Markup
Composition may be used to create a new class that both encapsulates an existing class, butand thenalso adds a value or field, the value of which must be equal for objects of this new class to be equal.  IfWhen objects of this new class are equated with objects of the encapsulated class, the new class must override the {{Object.equals()}} method; furthermore, and this method must follow the general contract for {{equals()}} as specified by the Java Language Specification \[[JLS 2005|AA. Bibliography#JLS 05]\].

An object is characterized both by its identity (location in memory) and by its state (actual data). The == operator compares only the identities of two objects (to check whether the references refer to the same object); the equals method defined in java.lang.Object can be customized by overriding to compare the state as well. When a class defines an equals() method, it implies that the method compares state. When the class lacks a customized equals() method (either locally declared, or inherited from a parent class), it uses the default Object.equals() implementation that is inherited from Object which compares only the references and may produce counter-intuitive results.

The equals() method applies only applies to objects, not primitives. There It is no need unnecessary to override the equals method when checking for logical equality is not useful. For example, enumerated types have a fixed set of distinct values that may be compared using == instead of rather than the equals() method. Note that enumerated types provide an equals() implementation that uses == internally; this default cannot be overridden. More generally, subclasses that both inherit an implementation of equals() from a superclass and also lack a requirement for additional functionality need not override the equals() method.

...

Never violate any of these requirements when overriding the equals() method. Mistakes resulting from a violation of the first requirements requirement are infrequent; consequently no we omit noncompliant code examples are provided for this case. Noncompliant We provide noncompliant code examples are provided for the second requirement (symmetry) and the third requirement (transitivity). The consistency requirement implies that mutable objects may not be unable to satisfy the equals() contract. Consequently, it is good practice to avoid defining equals() implementations that use unreliable data sources such as IP addresses and caches. The most common violation of the final requirement (regarding comparison with null) is equals() methods whose code throws an exception rather than returning false. This can constitute a security vulnerability (in the form of denial of service). The simple solution is to return false rather than to throw the exception.

...

By operating on String objects, the CaseInsensitiveString.equals() method violates the second contract requirement (symmetry). Because of the asymmetry, given a String object s and CaseInsensitiveString object cis that differ only differ in case, cis.equals(s)) returns true while s.equals(cis) returns false.

...

In this compliant solution, the CaseInsensitiveString.equals() method is simplified to operate only operate on instances of the CaseInsensitiveString class, consequently preserving symmetry.

Code Block
bgColor#ccccff
public final class CaseInsensitiveString {
  private String s;

  public CaseInsensitiveString(String s) {
    if (s == null) {
      throw new NullPointerException();
    }
    this.s = s;
  }

  public boolean equals(Object o) {
    return o instanceof CaseInsensitiveString &&
    ((CaseInsensitiveString)o).s.equalsIgnoreCase(s);
  }

  public static void main(String[] args) {
    CaseInsensitiveString cis = new CaseInsensitiveString("Java");
    String s = "java";
    System.out.println(cis.equals(s)); // Returns false now
    System.out.println(s.equals(cis)); // Returns false now
  }
}

...

In the noncompliant code example, p1 and p2 compare equal and p2 and p3 compare equal but p1 and p3 compare unequal; violating this violates the transitivity requirement. The problem is that the Card class has no knowledge of the XCard class and consequently cannot determine that p2 and p3 have different values for the field type. Unfortunately, it is impossible to extend an instantiable class (as opposed to an abstract class) by adding a value or field in the subclass while preserving the equals() contract.

Compliant Solution

Wiki Markup
BecauseIt it is impossible to extend an instantiable class while preserving the {{equals()}} contract,; use composition rather than mustinheritance beto usedachieve insteadthe ofdesired inheritanceeffect \[[Bloch 2008|AA. Bibliography#Bloch 08]\]. This compliant solution adopts this approach by adding a private {{card}} field to the {{XCard}} class and providing a {{public}} {{viewCard()}} method. 

...

Wiki Markup
*MET12-EX1:* This guideline may be violated provided that the incompatible types are notnever compared.  There are classes in the Java platform libraries (and elsewhere) that extend an instantiable class by adding a value component.  For example, {{java.sql.Timestamp}} extends {{java.util.Date}} and adds a nanoseconds field. The {{equals}} implementation for {{Timestamp}} violates symmetry and can cause erratic behavior if {{Timestamp}} and {{Date}} objects are used in the same collection or are otherwise intermixed." \[[Bloch 2008|AA. Bibliography#Bloch 08]\]

...

Guideline

Severity

Likelihood

Remediation Cost

Priority

Level

MET12-J

low

unlikely

medium

P2

L3

Automated Detection

...

Related Vulnerabilities

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

...