Issue with Comparator.comparing

Maurizio Cimadamore maurizio.cimadamore at oracle.com
Wed Dec 12 11:19:07 UTC 2018


Hi,
this issue has been reported by Remi on amber-dev, on a discussion on 
enhanced enums [1]. As suspected, I have been able to reproduce (and 
simplify) the problem on a vanilla JDK - here's the test case:

import java.util.*;
import java.util.stream.*;
import java.util.function.*;

class Foo<T extends Comparable<T>> {

     static Foo<String> S = new Foo<>("");
     static Foo<Integer> I = new Foo<>(42);

     private T t;

     Foo(T t) {
         this.t = t;
     }

     T t() {
         return t;
     }

     public static void main(String[] args) {
         List<Foo<?>> l = new ArrayList<>();
         l.add(S);
         l.add(I);
         Comparator<? super Foo<?>> comp = Comparator.comparing(Foo::t); 
//<---- ???
         Collections.sort(l, comp);
     }
}


This program compiles w/o warnings or errors, yet it fails at runtime 
with this:

Exception in thread "main" java.lang.ClassCastException: class 
java.lang.String cannot be cast to class java.lang.Integer 
(java.lang.String and java.lang.Integer are in module java.base of 
loader 'bootstrap')
     at java.base/java.lang.Integer.compareTo(Integer.java:59)
     at 
java.base/java.util.Comparator.lambda$comparing$77a9974f$1(Comparator.java:469)
     at 
java.base/java.util.TimSort.countRunAndMakeAscending(TimSort.java:355)
     at java.base/java.util.TimSort.sort(TimSort.java:220)
     at java.base/java.util.Arrays.sort(Arrays.java:1516)
     at java.base/java.util.ArrayList.sort(ArrayList.java:1749)
     at java.base/java.util.Collections.sort(Collections.java:177)
     at Foo.main(Test.java:25)


In other words, it seems like the type system is being tricked here into 
thinking that the comparator is working on an homogeneous type, while in 
reality there can be many types. I have a feeling that this is connected to:

https://bugs.openjdk.java.net/browse/JDK-8029307

(btw, the bug is marked as open, but the compiler dutifully applies the 
capture conversion described in the spec).

If I remove the capture conversion described in that issue, the compiler 
then will fail to compile the above example (as expected?):

error: incompatible types: inference variable U has incompatible bounds
         Comparator<? super Foo<?>> comp = Comparator.comparing(Foo::t); 
//<---- HERE
                                                               ^
     lower bounds: Comparable<? super U>
     lower bounds: ?
   where U,T are type-variables:
     U extends Comparable<? super U> declared in method 
<T,U>comparing(Function<? super T,? extends U>)
     T extends Object declared in method <T,U>comparing(Function<? super 
T,? extends U>)
1 error


To me, this looks related to this spec bug:

https://bugs.openjdk.java.net/browse/JDK-8170887

Dan, can you please confirm?

Thanks
Maurizio

[1] - 
http://mail.openjdk.java.net/pipermail/amber-dev/2018-December/003816.html



More information about the compiler-dev mailing list