Versions Compared

Key

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

Composition or inheritance may be used to create a new class that both encapsulates an existing class and adds one or more fields. When one class extends another in this way, the concept of equality for the subclass may or may not involve its new fields. That is, when comparing two subclass objects for equality, sometimes their respective fields must also be equal, and other times they need not be equal. Depending on the concept of equality for the subclass, the subclass might override equals(). Furthermore, this method must follow the general contract for equals(), as specified by the The Java Language Specification (JLS) [JLS 2015].

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 overridden 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 inherited from Object. The default Object.equals() implementation compares only the references and may produce unexpected results.

The equals() method applies only to objects, not to primitives.

Enumerated types have a fixed set of distinct values that may be compared using == 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 lack a requirement for additional functionality need not override the equals() method.

The general usage contract for equals(), as specified by the Java Language Specification JLS, establishes five requirements:

  1. It is reflexive: For any reference value x, x.equals(x) must return true.
  2. It is symmetric: For any reference values x and y, x.equals(y) must return true if and only if y.equals(x) returns true.
  3. It is transitive: For any reference values x, y, and z, if x.equals(y) returns true and y.equals(z) returns true, then x.equals(z) must return true.
  4. It is consistent: For any reference values x and y, multiple invocations of x.equals(y) consistently return true or consistently return false, provided no information used in equals() comparisons on the object is modified.
  5. For any non-null reference value x, x.equals(null) must return false.

Never violate any of these requirements when overriding the equals() method.

...

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 a CaseInsensitiveString object cis that differ only in case, cis.equals(s)) returns true, while s.equals(cis) returns false.

Compliant Solution

In this compliant solution, the CaseInsensitiveString.equals() method is simplified to operate only 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 int hashCode() {/* ... */}

  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
  }
}

...

Unfortunately, in this case it is impossible to extend the Card class by adding a value or field in the subclass while preserving the equals() contract. This problem is not specific to the Card class , but applies to any class hierarchy that can consider equal instances of distinct subclasses of some superclass. For such cases, use composition rather than inheritance to achieve the desired effect [Bloch 2008]. This compliant solution adopts this approach by adding a private card field to the XCard class and providing a public viewCard() method.

...

If the Card.equals() method could unilaterally assume that two objects with distinct classes were not equal, it could be used in an inheritance hierarchy while preserving transitivity. .:

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

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

  public boolean equals(Object o) {
    if (!(o.getClass() == this.getClass())) {
      return false;
    }

    Card c = (Card)o;
    return c.number == number;
  }

  public int hashCode() {/* ... */}

}

...

A uniform resource locator (URL) specifies both the location of a resource and also a method to access it. According to the Java API documentation for class Class URL [API 20062014]:

Two URL objects are equal if they have the same protocol, reference equivalent hosts, have the same port number on the host, and the same file and fragment of the file.

Two hosts are considered equivalent if both host names can be resolved into the same IP addresses; else if either host name can't be resolved, the host names must be equal without regard to case; or both host names equal to null.

...

Consider an application that allows an organization's employees to access an external mail service via http://mailwebsite.com. The application is designed to deny access to other websites by behaving as a makeshift firewall. However, a crafty or malicious user could nevertheless access an illegitimate website http://illegitimatewebsite.com if it were hosted on the same computer as the legitimate website and consequently shared the same IP address. Even worse, if the legitimate website were hosted on a server in a commercial pool of servers, an attacker could register multiple websites in the pool (for phishing purposes) until one was registered on the same computer as the legitimate website, consequently defeating the firewall.

...

This compliant solution compares the string representations of two URLs' string representations, thereby avoiding the pitfalls of URL.equals().:

Code Block
bgColor#ccccff
public class Filter {
  public static void main(String[] args) throws MalformedURLException {
    final URL allowed = new URL("http://mailwebsite.com");
    if (!allowed.toString().equals(new URL(args[0]).toString())) {
      throw new SecurityException("Access Denied");
    }
    // Else proceed
  }
}

...

A Uniform Resource Identifier (URI) contains a string of characters used to identify a resource; this is a more general concept than an URL. The java.net.URI class provides string-based equals() and hashCode() methods that satisfy the general contracts for Object.equals() and Object.hashCode(); they do not invoke hostname resolution and are unaffected by network connectivity. URI also provides methods for normalization and canonicalization that URL lacks. Finally, the URL.toURI() and URI.toURL() methods provide easy conversion between the two classes. Programs should use URIs instead of URLs whenever possible. According to the Java API Class URI documentation [API 20062014] URI class documentation:

A URI may be either absolute or relative. A URI string is parsed according to the generic syntax without regard to the scheme, if any, that it specifies. No lookup of the host, if any, is performed, and no scheme-dependent stream handler is constructed.

This compliant solution uses a URI object instead of a URL. The filter appropriately blocks the website when presented with any string other than http://mailwebsite.com because the comparison fails.

...

Additionally, the URI class performs normalization (removing extraneous path segments like such as '..') and relativization of paths [API 20062014] and , [Darwin 2004].

Noncompliant Code Example (java.security.Key)

...

This noncompliant code example compares two keys using the equals() method. The comparison may return false even when the key instances represent the same logical key.

...

MITRE CWE

CWE-697, Insufficient Comparison

Bibliography

 

2006Method
[API 2014]Class URI
Class URL
(method equals())

[Bloch 2008]

Item 8, "Obey the General Contract When Overriding equals"

[Darwin 2004]

Section 9.2, "Overriding the equals Method"

[Harold 1997]

Chapter 3, "Classes, Strings, and Arrays," section "The Object Class (Equality)"

[Sun 2006]

Determining If Two Keys Are Equal (JCA Reference Guide)

[Techtalk 2007]

"More Joy of Sets"

...