equals method must be:
- reflexive: for any non-null reference value
x,x.equals(x)should returntrue. - symmetric: for any non-null reference values
xandy,x.equals(y)should returntrueif and only ify.equals(x)returnstrue. - transitive: for any non-null reference values
x,y, andz, ifx.equals(y)returnstrueandy.equals(z)returnstrue, thenx.equals(z)should returntrue. - consistent: for any non-null reference values
xandy, multiple invocations ofx.equals(y)consistently returntrueor consistently returnfalse, provided no information used inequalscomparisons on the objects is modified. - return
falseforx.equals(null), for any non-null reference valuex.
@Test
public void testEquals() {
// reflexive
assertTrue(x.equals(x));
// symmetric
assertTrue(x.equals(y) == y.equals(x));
// transitive
if (x.equals(y) && y.equals(z)) {
assertTrue(x.equals(z));
}
// consistent
assertTrue(x.equals(y) == x.equals(y));
// null check
assertFalse(x.equals(null));
}
Implementing Equals(): Approach 1The steps are:
- Check reference equality (good optimisation step)
- Check correct class type using
instanceof - Cast to correct type
- Compare objects
@Override
public boolean equals(Object obj) {
// check reference equality
if (this == obj) {
return true;
}
// check correct arg type
if (!(obj instanceof Rectangle)) {
return false;
}
// cast the object to the correct type
Rectangle other = (Rectangle) obj;
// compare fields
return other.getLength() == length &&
other.getWidth() == width;
}
But what happens if you extend this class by adding another field? For example, a Rectangle has a length and width. But what if we create a Cuboid that extends Rectangle with an additional depth attribute?
The problem occurs when you mix objects of Rectangle and Cuboid because Rectangle instanceof Cuboid returns false, whereas Cuboid instanceof Rectangle returns true. This breaks the symmetric rule because rectangle.equals(cuboid) is true, but cuboid.equals(rectangle) is false. In order to preserve symmetry, we can change our equals method as follows:
Implementing Equals(): Approach 2- Check if null
- Check correct class type using
getClass - Cast to correct type
- Compare objects
@Override
public boolean equals(Object obj) {
// check null
if (obj == null) {
return false;
}
//check correct type
if(getClass() != obj.getClass()) {
return false;
}
// cast the object to the correct type
Rectangle other = (Rectangle) obj;
// compare fields
return other.getLength() == length &&
other.getWidth() == width;
}
The getClass method will always return false if the parameter is not the exact same type as the object class.
In most cases, you should use Approach 2 so that you obey the equals contract, unless you want to compare subclasses with their base types.
No comments:
Post a Comment
Note: Only a member of this blog may post a comment.