Equality

The basics
All classes in Java have an equals() method with the following prototype:
public boolean equals(Object obj);Everybody Loves Defaults
The equals() method is defined in the Object class, so every class inherits a default implementation, which merely compares by reference:
public boolean equals(Object obj) {
return (this == obj);
}
This is rarely useful, so classes will often want to override this with their own implementation.
Note that custom implementations still must take an instance of Object.
The contract of equals()
In a correct implementation of equals(), the following must hold true:
- 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.
Properly Implementing Equals
Josh Bloch's Effective Java is pretty much the canonical source for this. Eclipse's tool for generating hashCode() and equals() uses its recommendations.
Steps:
- Check this == other, return true if so
- Check other == null, return false if so
- Check if the type of other is compatible, return false if not
- Cast Object parameter to appropriate type
- Compare members and return false if any are different
- Return true if we got all the way through
Reflexivity
For any non-null reference value x, x.equals(x) should return true.
We can achieve this by starting our equals() with this check:
if (this == obj)
{
return true;
}
Null Safety
For any non-null reference value x, x.equals(null) should return false.
We can use this as the second check in our equals() implementation.
if (obj == null)
{
return false;
}
Symmetry
For any non-null reference values x and y, x.equals(y) should return true if and only if y.equals(x) returns true.
This is actually tricky - what do you do if the right-hand-side is a subclass of your class? The Liskov Substitution Principle says that we should accept a subclass anywhere we accept a superclass. But what if the subclass adds fields?
Your third check should be:
if (getClass() != obj.getClass())
// OR
if (!(obj instanceof MyClass))
{
return false;
}Symmetry, Cont.
Some more discussion on getClass() vs instanceof:
Transitivity
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.
As long as you follow all the other rules, you shouldn't have a problem with this one - unless you're using instanceof instead of getClass().
Consistency
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.
This should be pretty easy:
- Don't use any random numbers in your equals()
- Don't call anything that has side effects
- Don't rely on anything located outside your object
Comparing members
After all the boilerplate, your equals() method needs to actually compare the pieces of your objects.
Note that you don't have to check everything! You can decide what fields constitute "identity".
Primitive Members
Easy to compare. Just use !=.
public static class MyClass {
public int i;
@Override
public boolean equals(Object obj) {
// ...
MyClass other = (MyClass) obj;
if (i != other.i) {
return false;
}
// ...
return true;
}
}
Object Members
More work, since they could potentially be null (on both sides).
If you know something can't be null, you could simplify this.
public static class MyClass {
public String s;
@Override
public boolean equals(Object obj) {
// ...
MyClass other = (MyClass) obj;
if (s == null) {
if (other.s != null) {
return false;
}
}
else if (!s.equals(other.s)) {
return false;
}
// ...
return true;
}
}What about hashCode?
The JavaDocs state:
"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."
and
"Note that it is generally necessary to override the hashCode method whenever [equals] is overridden, so as to maintain the general contract for the hashCode method, which states that equal objects must have equal hash codes."
hashCode is not on the JCA exam.
Equality
By mitchellmebane
Equality
- 359