> De: "John Rose" <john.r.r...@oracle.com> > À: "Remi Forax" <fo...@univ-mlv.fr> > Cc: "Peter Levart" <peter.lev...@gmail.com>, "Rémi Forax" > <fo...@openjdk.java.net>, "core-libs-dev" <core-libs-dev@openjdk.java.net> > Envoyé: Jeudi 3 Juin 2021 22:51:28 > Objet: Re: RFR: 8199318: add idempotent copy operation for Map.Entry
> On Jun 3, 2021, at 12:46 PM, Remi Forax < [ mailto:fo...@univ-mlv.fr | > fo...@univ-mlv.fr ] > wrote: >> I kind of regret that the compiler does not provide automatically an >> implementation of compareTo if the record implements Comparable. >> People sucks at writing compareTo and the resulting bugs are hard to >> find/reproduce. > That’s a slippery slope. IIRC we consciously stopped > before that step. > That said, there are other ways to fix this. We should > have utilities (maybe in the JDK but not the JLS) which > build such methods and make it easy for users to grab onto > them. Maybe something like this: > interface ComparableRecord<T extends Record & ComparableRecord<T>> > extends Comparable<T> { … } > record Foo(int x, String y) implements ComparableRecord<Foo> { … } > [ http://cr.openjdk.java.net/~jrose/draft/ComparableRecord.java | > http://cr.openjdk.java.net/~jrose/draft/ComparableRecord.java ] The main issue with this kind of code is that the JIT does not see through the ClassValue. Tweaking a little bit your code, I get (It's a PITA that we have to use a raw type to workaround circularly defined parameter type) import java.util.ArrayList; import java.util.Comparator; import java.util.List; import java.util.stream.Stream; @SuppressWarnings({"rawtypes","unchecked"}) interface ComparableRecord<T extends Record & ComparableRecord<T>> extends Comparable<T> { @Override default int compareTo(T that) { if (this.getClass() != that.getClass()) { throw new IllegalArgumentException("not same class"); } return COMPARE_TO_METHODS.get(this.getClass()).compare(this, that); } static final ClassValue<Comparator<Object>> COMPARE_TO_METHODS = new ClassValue<>() { @Override protected Comparator<Object> computeValue(Class<?> type) { return Stream.of(type.getRecordComponents()) .map(component -> { var accessor = component.getAccessor(); return Comparator.<Object, Comparable>comparing(r -> { try { return (Comparable<?>) accessor.invoke(r); } catch (ReflectiveOperationException ex) { throw new IllegalArgumentException(ex); } }); }) .reduce((r1, r2) -> 0, Comparator::thenComparing, (_1, _2) -> { throw null; }); } }; static void main(String[] args) { record Foo(int x, String y) implements ComparableRecord<Foo> { } var list = Stream.of(new Foo(2, "foo"), new Foo(2, "bar")) .sorted().toList(); System.out.println(list); } } > — John