A library for implementing equals and hashCode
Brian Goetz
brian.goetz at oracle.com
Thu Apr 25 14:20:17 UTC 2019
(late to the party)
Thanks for pulling this together. That people routinely implement equals/hashCode explicitly is something we would like to put in the past.
Let me make a few points regarding connection with future potential features, not because I want to say that these will obsolete Equivalence before it starts, or that we should delay working on it until we have these features — just to point out areas of potential overlap so we can pay attention to how these things might converge.
1. Pattern matching. Pattern matching offers a path to a better way to write equals() methods, without the complex control flow that is typical of template-expanded implementations (whether expanded by a human or an IDE.). For a class like Point, we can implement equals via:
boolean equals(Object o) {
return o instanceof Point p
&& p.x == x
&& p.y = y;
}
This is no less explicit than the status quo, but more readable and less error-prone (no short circuit tests; one big &&'ed expression.). However, pattern matching offers little help for implement hashCode in a comparable way.
2. Pattern matching, again. The implementation of a nontrivial pattern is a class member. For sake of exposition, imagine it is declared like this (please, we’re not discussing the syntax of this now):
class Point {
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public pattern Point(int x, int y) {
x = this.x;
y = this.y;
}
}
(and of course records will automate this.). Given that a pattern is a way of extracting a bundle of state from an object, assuming there were some way to refer symbolically to a pattern, one could derive an Equivalence from a “pattern reference”:
Equivalence.of(<pattern ref for Point deconstruction pattern>);
3. Field references. We like the Comparator factories and combinators well enough, which take lambdas that extract single fields. It’s a short hop to accepting field references, which would be both more pleasant to use, and more optimizable (field references are more amenable to constant-folding optimizations, for example, by virtue of their transparency.)
4. Specialized generics. The rift between objects and primitives was the major pain-generator for all of the Java-8-era APIs, including the Comparator APIs. Not only did we need hand-specialized comparingInt() methods, but the lack of a common super type without boxing meant that we could not use the more appealing approach of varargs, but instead had to have a method call for each component in the comparison. You are in the same boat now, until Valhalla delivers specialized generics.
Its worth thinking a bit about what we would like the long-term API to look like, so we can steer clear of getting in its way between now and then. With specialized generics, we’d probably want something like
static<T> Equivalence<T> of(Class<T> clazz, Function<T,?>… components)
Which suggests we probably want to steer away from having a varargs option, so that we are not buying ourselves one more migration headache.
> On Apr 22, 2019, at 2:29 PM, Liam Miller-Cushon <cushon at google.com> wrote:
>
> Please consider this proposal for a library to help implement equals and hashCode.
>
> The doc includes a discussion of the motivation for adding such an API to the JDK, a map of the design space, and some thoughts on the subset of that space which might be most interesting:
>
> http://cr.openjdk.java.net/~cushon/amber/equivalence.html <http://cr.openjdk.java.net/~cushon/amber/equivalence.html>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.java.net/pipermail/amber-spec-experts/attachments/20190425/c3f6cff7/attachment.html>
More information about the amber-spec-experts
mailing list