The contract of equals with regards to null
, is as follows:
For any non-null reference value
x
,x.equals(null)
sho
In the first case o1.equals(o2)
returns false because o1
is not equal to o2
, which is perfectly fine. In the second case, it throws NullPointerException
because o2
is null
. One cannot call any method on a null
. It may be a limitation of programming languages in general, but we have to live with it.
It is also not a good idea to throw NullPointerException
you are violating the contract for the equals
method and making things more complex than it has to be.
You should return false
if the parameter is null
.
To show that this is the standard, see 'Objects.equals(Object, Object)
from java.util
, that performs an assymetric null check on the first parameter only (on which equals(Object)
will be called). From the OpenJDK SE 11 source code (SE 1.7 contains exactly the same):
public static boolean equals(Object a, Object b) {
return (a == b) || (a != null && a.equals(b));
}
This also handles two null
values as equal.
Personally, I'd rather it perform as it does.
The NullPointerException
identifies that the problem is in the object against which the equals operation is being performed.
If the NullPointerException
was used as you suggest and you tried the (sort of pointless) operation of...
o1.equals(o1)
where o1= null...
Is the NullPointerException
thrown because your comparison function is screwed or because o1 is null but you didn't realise?
An extreme example, I know, but with current behaviour I feel you can tell easily where the problem lies.
To the question of whether this asymmetry is inconsistent, I think not, and I refer you to this ancient Zen kōan:
At that moment, the compiler reached enlightenment.
Note that the contract is "for any non-null reference x". So the implementation will look like:
if (x != null) {
if (x.equals(null)) {
return false;
}
}
x
need not be null
to be deemed equal to null
because the following definition of equals
is possible:
public boolean equals(Object obj) {
// ...
// If someMember is 0 this object is considered as equal to null.
if (this.someMember == 0 and obj == null) {
return true;
}
return false;
}
An exception really should be an exceptional situation. A null pointer might not be a programmer error.
You quoted the existing contract. If you decide to go against convention, after all this time, when every Java developer expects equals to return false, you'll be doing something unexpected and unwelcome that will make your class a pariah.
I could't disagree more. I would not rewrite equals to throw an exception all the time. I'd replace any class that did that if I were its client.