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.