2

In this code I have an error on the second line while the first one successfully compiles:

Comparator<? super Integer> a = (x, y) -> Integer.compare(x, y);
Comparator<? super Integer> b = a.thenComparing((x, y) -> Integer.compare(x, y));

The error is "incompatible types: Object cannot be converted to int"

thenComparing has following signature: thenComparing(Comparator<? super T> other), so as I understand other in this context will become something like Comparator<? super super T> or Comparator<? super super Integer>.

Why in my example it becomes Comparator<Object>?

Is this a compiler flaw or is it guarding me from something?

Dibro
  • 45
  • 2
  • 6

1 Answers1

4

You're defining a as a Comparator<? super Integer>, and your assignment (x, y) -> Integer.compare(x, y) fulfills it.

However, the compiler does not know what exact type ? is. It could be an Object, a Number or an Integer itself, but which one is not known. thenComparing() relies on the type argument of a, for its signature is Comparator<T> thenComparing(Comparator<? super T>). You are passing in a Comparator<Integer>, but a's type argument does not match.

Generics are invariant. A Comparator<? super Integer> is not a Comparator<Integer>. Use the wildcard only if you don't care about the type. Otherwise, use the proper type argument.

Define a as a Comparator<Integer> in order to fix it.


Here's an excellent answer to another Stackoverflow post, explaining why generics are not implicitly polymorphic. It also explains what would be the problem if generics were.

MC Emperor
  • 22,334
  • 15
  • 80
  • 130
  • So this is a compiler limitation? And such code would be safe otherwise? – Dibro May 26 '20 at 14:17
  • It is an *intended* compiler limitation: the compiler stops you because it cannot guarantee type safety anymore. – MC Emperor May 26 '20 at 14:19
  • But is there an example where this code could lead to a runtime error? – Dibro May 26 '20 at 14:23
  • 2
    Why use `? super Integer`? Integer is a final class so it can't be the super class of anything. Just use `Integer`. – WJS May 26 '20 at 14:34
  • @WJS Just an example. It could be any class. – Dibro May 26 '20 at 14:36
  • Well, I cannot think of such an example at the moment, but the thing is that the compiler cannot *prove* that this is right. Just like `if (myAnimal instanceof Dog) { myAnimal.bark(); }` won't compile either. – MC Emperor May 26 '20 at 14:51
  • I've found a good example why this limitation makes sense: `List super Integer> myList = Arrays.asList(42, new Object());` `myList.sort((x, y) -> Integer.compare(x, y));` Where `List.sort(...)` has the same signature as `Comparator.thenCompare(...)`. – Dibro May 26 '20 at 15:12