RFR: 8199318: add idempotent copy operation for Map.Entry

forax at univ-mlv.fr forax at univ-mlv.fr
Thu Jun 3 21:49:40 UTC 2021


> De: "John Rose" <john.r.rose at oracle.com>
> À: "Remi Forax" <forax at univ-mlv.fr>
> Cc: "Peter Levart" <peter.levart at gmail.com>, "Rémi Forax"
> <forax at openjdk.java.net>, "core-libs-dev" <core-libs-dev at 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:forax at univ-mlv.fr |
> forax at 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


More information about the core-libs-dev mailing list