==
is symmetric; that is to say, for any values x
and y
, (x == y) == (y == x)
. This is a guarantee provided to us by the JLS §15.21.1 for numbers, and §15.21.3 for reference types (or everything that isn't a primitive value).
It could also be seen as transitive, in that if three values x, y, z
exist, and x == y && y == z
, then x == z
. This is again provided by the same JLS specification - merely repeated to mitigate the issue of the common variable y
.
The real problem here comes with regards to autoboxing; when you go to unbox null
, then by the JLS, you're going to get a NullPointerException
- independent of the comparison operation you're going to do next.
Effectively:
You have a boxed primitive type on one side of the comparison, and a primitive on the other. The value of either isn't yet considered.
Given that the value of the primitive will force numerical comparison due to it being a boxed primitive, Java will then try to unbox the boxed value.
You can't unbox null
, hence NullPointerException
.
This is (kind of) where equals()
steps in - by its contract, two non-null instances must be equivalent to each other if they are indeed the same thing. If either (but not both) of these values are null
, then they're not the same instances.
I say "kind of" since there's really nothing to enforce the supposed contract on Object#equals
; you could (with some effort) write an asymmetric equals()
method, although one would wonder why you would want to.
false
if the argument isnull
.) – Kamseen*
being "symmetric" means thatx*x
holds true, but I may remember that wrong. – Hesitancyx*x
holding true would make it reflexive. – Kamseen==
. Whether it matters for.equals()
is up to the implementation of.equals()
, but best practice would say thata.equals(b) == b.equals(a)
– Carberry