(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 <cus...@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>