Language Design: Equality & Identity
Part 5: Java
The recent development of Java is an interesting case, as it faces these questions more acutely with the introduction of value types in Project Valhalla.
Recap
Java-before-value-types worked like this:
== | Object#equals | |
---|---|---|
primitive types (int , float , byte , …) |
primitive equality | not available |
reference types (java.lang.Integer , java.lang.Float , java.lang.Byte , …) |
reference equality | value equality |
Note that every primitive type has a reference-based “wrapper type” to which it can be converted.
When the compiler injects this conversion implicitly, it is called “auto-boxing”.
double d = 1.0; // primitive value
1.0 == 1.0; // primitive equality → true
Double D = d; // auto-boxing the primitive value into its wrapper type
D == D // reference equality, same reference → true
D == new Double(1.0) // reference equality, different reference → false
D.equals(new Double(1.0)) // value equality → true
Primitive equality of primitives behaves largely the same as the value equality of its wrappers, except for floating point values, where behavior differs for zero and not-a-number values:
0.0 == -0.0 // true
new Double(0.0).equals(new Double(-0.0)) // false
Double.NaN == Double.NaN // false
new Double(Double.NaN).equals(new Double(Double.NaN)) // true
A (side-)effect of this is that a HashMap<Double>
can reliably find entries, including ones with a NaN
key.
What’s new
With Valhalla, Java …
- allows programmers to define their own value types,
- migrates primitive wrapper types into value types, and
- defines
==
on value types to be a member-wise check for “sameness”.1
This means that comparison between two wrapper values with the same wrapped value now always return true
, including NaN
values:2
before Valhalla | after Valhalla | |
---|---|---|
new Double(Double.NaN) == new Double(Double.NaN) |
false | true |
new Double(Double.NaN).equals(new Double(Double.NaN)) |
true | true |
This allows a more efficient representation of previously boxed wrapper types, without introducing breaking changes.
-
“Two value objects are the same if they are instances of the same class and the values of their instance fields are the same.” (draft spec) ↩
-
“If
a
andb
are variables storing the results of any two boxing conversions ofp
, then it is always the case thata.equals(b)
anda == b
.” (draft spec) ↩