equals
method must be:
- reflexive: for any non-null reference value
x
,x.equals(x)
should returntrue
. - symmetric: for any non-null reference values
x
andy
,x.equals(y)
should returntrue
if and only ify.equals(x)
returnstrue
. - transitive: for any non-null reference values
x
,y
, andz
, ifx.equals(y)
returnstrue
andy.equals(z)
returnstrue
, thenx.equals(z)
should returntrue
. - consistent: for any non-null reference values
x
andy
, multiple invocations ofx.equals(y)
consistently returntrue
or consistently returnfalse
, provided no information used inequals
comparisons on the objects is modified. - return
false
forx.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 1
The 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.