compareTo and deriving

Guy Steele guy.steele at
Tue Dec 5 16:29:42 UTC 2017

> On Dec 5, 2017, at 10:17 AM, Brian Goetz <brian.goetz at> wrote:
> At some level, this goes to the heart of "why should records, and not tuples, be our 'plain data' abstraction."  Adding behavior to a record, **where that behavior is derived strictly from the state**, is easy and natural.
> You can look at the "but I have to write compareTo" issue as glass 90% full or 10% empty; a record starts life with a sensible ctor, dtor, accessors, equals, hashCode, and toString.  So adding compareTo is pretty easy.  Note also that there's no way we can reasonably derive compareTo from the state description, since (a) not all state fields are going to be relevant for ordering comparison and (b) the order in which they are declared may not be the natural comparison order.  So somewhere in the program the user is going to have to write down that "a Person is compared by (lastName, firstName)", somehow.
> We could of course invent all sorts of shorthands, but I think the return-on-complexity there is limited, as the natural way to say this is pretty straightforward:
>     record Person(String first, String last, String age)
>             implements Comparable<Person> {
>         private static final Comparator<Person> cc
>             = Comparators.comparing(Person::last).thenComparing(Person::first);
>         public int compareTo(Person other) {
>             return cc.compareTo(other);
>         }
>     }
> But, let's not get distracted by Billy, we care about semantics. Where are the pitfalls you see of this approach where it might run afoul of the "consistency between equals and compareTo is recommended" dictum?

If the automatically generated `equals` method examines all fields but the `compareTo` method does not, then there can be cases where `compareTo` says that two objects are equal but `equals` says they are unequal.


		Person me = new Person(“Guy”, “Steele”, 63);
		Person myDad = new Person(“Guy”, “Steele”, 89);

		me.equals(myDad) ==> false
		me.compareTo(myDad) ==> 0

One possible solution is simply a programming discipline or documentation suggestion that says that if you write a `compareTo` method, with or without a `Comparator`, you should at least think about whether to provide an explicit definition of `equals` as well to ensure that they are consistent.

In fact, we already have such a note in the documentation for `Comparator`:

    The natural ordering for a class C is said to be consistent with equals if and only if e1.compareTo(e2) == 0 has the same boolean value as e1.equals(e2) for every e1 and e2 of class C. . . .

    It is strongly recommended (though not required) that natural orderings be consistent with equals. . . .

    Virtually all Java core classes that implement Comparable have natural orderings that are consistent with equals.

So I think the question that Rémi raises has to do with whether the language design going forward can/should better assist programmers to address this already existing recommendation.


More information about the amber-spec-experts mailing list