saga
saga

Reputation: 93

Why can Object.equals compare types that cannot be compared with the reference equality operator (==)?

I know in Java you can't compare two unrelated instances using ==, since it produces a compilation error (incompatible types). So for example

class Animal {}
class Dog extends Animal {}
class Cat extends Animal {}

Dog d = new Dog();
Cat c = new Cat();
System.out.println( d == c );

is a compile-time error.

But why doesn't using equals inherited from Object produce that kind of error:

System.out.println( d.equals(c) );  // is false

even though Object.equals internally uses ==?

Now, I know why it's false, but I don't know why it's not an error, since the signature is:

public boolean equals(Object obj) {
    return (this == obj);
}

Upvotes: 2

Views: 1513

Answers (2)

M. Justin
M. Justin

Reputation: 21162

The compile time check is performed at the site of the reference equality — within Object.equals. In the context of the Object.equals method you referenced, it is comparing this with an Object:

public boolean equals(Object obj) {
    return (this == obj);
}

Per the Java Language Specification:

It is a compile-time error if it is impossible to convert the type of either operand to the type of the other by a casting conversion (§5.5). The run-time values of the two operands would necessarily be unequal (ignoring the case where both values are null).

Since this can be cast to Object, this == obj is a valid usage of the reference equality operator (==), and therefore it compiles.


For a practical example, observe a case within the Java API where two objects cannot be compared via reference equality (==), but which are equal when compared via Object.equals.

First, note that the implementation of Object.equals is just the base implementation of equals. Subclasses of Object are encouraged to use a different implementation, if appropriate.

Second, note that unlike the reference equals operator (==), the equals method has no restriction on whether two compared objects can be cast to the type of the other. Per the Javadocs of Object.equals, the two objects need only implement an equivalence relation:

The equals method implements an equivalence relation on non-null object references:

  • It is reflexive: for any non-null reference value x, x.equals(x) should return true.
  • It is symmetric: for any non-null reference values x and y, x.equals(y) should return true if and only if y.equals(x) returns true.
  • It is transitive: for any non-null reference values x, y, and z, if x.equals(y) returns true and y.equals(z) returns true, then x.equals(z) should return true.
  • It is consistent: for any non-null 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 objects is modified.
  • For any non-null reference value x, x.equals(null) should return false.

Take the Java List interface. It specifies how implementing classes must implement equals:

Compares the specified object with this list for equality. Returns true if and only if the specified object is also a list, both lists have the same size, and all corresponding pairs of elements in the two lists are equal. (Two elements e1 and e2 are equal if Objects.equals(e1, e2).) In other words, two lists are defined to be equal if they contain the same elements in the same order. This definition ensures that the equals method works properly across different implementations of the List interface.

Now, take two lists that can't be cast to one another, such as ArrayList and LinkedList. A LinkedList reference cannot be compared with an ArrayList reference using the reference equality error (==), since neither type can be cast to the other. On the other hand, an ArrayList will be .equals to a LinkedList if they contain the same elements in the same order.

ArrayList<String> a = new ArrayList<String>(Arrays.asList("X", "Y", "Z"));
LinkedList<String> b = new LinkedList<String>(Arrays.asList("X", "Y", "Z"));

assert a.equals(b); // true
assert a == b; // compiler error

Upvotes: 0

Ga&#235;l J
Ga&#235;l J

Reputation: 15105

Because inside Object#equals the types you compare are both Object, thus the == operator is "allowed".

Remember though that you most of the time don't want to compare using == or the default Object#equals as it compares the references, not the content. See https://stackoverflow.com/questions/7520432/what-is-the-difference-between-and-equals-in-java?r=SearchResults&s=3|156.7237

Upvotes: 2

Related Questions