60. Rewriting equals() via type patterns for instanceof
It is not mandatory to rely on instanceof
to implement the equals()
method, but it is a convenient approach to write something as follows:
public class MyPoint {
private final int x;
private final int y;
private final int z;
public MyPoint(int x, int y, int z) {
this.x = x;
this.y = y;
this.z = z;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof MyPoint)) {
return false;
}
final MyPoint other = (MyPoint) obj;
return (this.x == other.x && this.y == other.y
&& this.z == other.z);
}
}
If you are a fan of the previous approach for implementing equals()
, then you’ll love rewriting it via a type pattern for instanceof
. Check out the following snippet:
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
return obj instanceof MyPoint other
&& this.x == other.x && this.y == other.y
&& this.z == other.z;
}
If MyPoint
is generic (MyPoint<E>
) then simply use a wildcard as follows (more details are available in the next problem):
return obj instanceof MyPoint<?> other
&& this.x == other.x && this.y == other.y
&& this.z == other.z;
Cool, right?! However, pay attention that using instanceof
to express the equals()
contract imposes the usage of a final
class of final equals()
. Otherwise, if subclasses are allowed to override equals()
, then instanceof
may cause transitivity/symmetry bugs. A good approach is to pass equals()
through a dedicated verifier such as equals verifier (https://github.com/jqno/equalsverifier), which is capable of checking the validity of the equals()
and hashCode()
contracts.