equals
and hashCode
in Java (updated link). His implementations draw heavily on Joshua Bloch’s principles. I think he’s done a great job in implementing the Java standard implementation of equality. On the other hand, I’m a little disquieted by the way equality works in Java.TempBean
, that has members _foo
, and _bar
:
public boolean equals(Object object) {
if (object != this) {
if (object != null && getClass().equals(object.getClass())) {
final TempBean other = (TempBean) object;
return (_foo == null ? other._foo == null : _foo.equals(other._foo)) &&
(_bar == null ? other._bar == null : _bar.equals(other._bar));
}
return false;
}
return true;
}
getClass().equals(object.getClass())
. Compare and contrast to:
public boolean equals(Object object) {
if (object != this) {
if (object != null && object instanceof TempBean) {
final TempBean other = (TempBean) object;
return (_foo == null ? other._foo == null : _foo.equals(other._foo)) &&
(_bar == null ? other._bar == null : _bar.equals(other._bar));
}
return false;
}
return true;
}
object
is a TempBean
. That’s almost the same as the first code. It’s different when object
is an instance of TempBean
but not a TempBean
.
void doSomethingSeussian (final TempBean thingOne) {
final TempBean thingTwo = TempBean.getThingFromBox(/* ... */);
if (thingTwo.equals(thingOne)) {
// ...
}
}
thingTwo
is obviously a TempBean
. Or is it? What does the factory method getThingFromBox
do? And what if thingOne
is an instance of a subclass of TempBean
? What if it has exactly the same _foo
and _bar
as thingTwo
? Why isn’t it equals
? thingOne
is a ThreadSafeTempBean
that guarantees thread safety in a way that is otherwise transparent to clients? Or a MockTempBean
that is used for unit tests?==
is not the same thing as equals
. One measures identity , whether two objects are the exact same identical object in memory at the same location. (Unless they are those dratted primitives, in which case it measures value equality). Identity is almost useless in Java, it breaks all over the place, and that’s why it’s rightfully rare.equals
captures physical equality, which ignores the Liskov Substitution Principle, one of the fundamental principles in OO design. It is leaking the implementation, just like a leaky abstraction.thingOne.eqauls(thingTwo)
is not always the same as thingTwo.equals(thingOne)
! And if you’re implementing a HashMap
, which kind of equality do you want to use for deciding whether a key is already in the collection?HashMap
that respected the Liskov Substitution Principle for keys. And I can also think of code where I want a map or set that respects the Liskov substitution principle.TempBeans
is the TempBean
class (which is also the TempBean
interface in Java, but Java doesn’t allow code in interfaces). We’re off to a good start, the code for equals
is in the TempBean class. But where we went wrong is making it an instance method. What happens if it’s a static method?
public static boolean equivalent(TeampBean left, TempBean right) {
if (left != right) {
if (left != null && right != null) {
return (left._foo == null ? right._foo == null : left._foo.equals(right._foo)) &&
(left._bar == null ? right._bar == null :left. _bar.equals(right._bar));
}
return false;
}
return true;
}
void doSomethingSeussian (final TempBean thingOne) {
final TempBean thingTwo = TempBean.getThingFromBox(/* ... */);
if (TempBean.equivalent(thingTwo, thingOne)) {
// ...
}
}
TempBean
, a ThreadSafeTempBean
, or a MockTempBean
. It’s true that sometimes you care very deeply about the difference. But most of the time, you probably do not, and using an equals
method that does care is wrong.Labels: java