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>

More information about the amber-spec-observers mailing list