thenComparing function throws 'no instance(s) of type variable(s) U exist so that Object conforms to Comparable<? super U>'

What is a proper usage of the thenComparing function and why this option is not working properly? I dont quite understand the errors that are thrown…

Error: no instance(s) of type variable(s) U exist so that Object conforms to Comparable<? super U>

    cards.stream()
            .collect(Collectors.groupingBy(card -> card.kind.rank, Collectors.counting()))
            .entrySet().stream()
            .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()).thenComparing(Map.Entry::getKey))
            .forEachOrdered(e -> sortedCards.put(e.getKey(), e.getValue()));

My card class:

    public class Card implements Comparable<Card> {
    
        public final Kind kind;
// constructors
    }

Kind class:

    public enum Kind {
        TWO(1, "2"),
        THREE(2, "3"),
        FOUR(3, "4"),
// etc.
// constructos
}

>Solution :

The reason of the compilation error you’ve encountered is a type inference issue.

The when you’re chaining methods while constructing a comparator, the compiler fails to infer the type of the method reference from the target type.

You can resolve it by using type-witness, i.e. providing the types of key and value explosively in the angle brackets <K,V> (these are the types declared by comparingByValue() which produces the first comparator in the chain):

Map.Entry.<Integer, Long>comparingByValue(Comparator.reverseOrder())
    .thenComparing(Map.Entry::getKey)

Also, instead of forEachOrdered() it would be better to use collect() and generate a Map as a result of the stream execution then populate pre-created map via side-effects. It makes your code more verbose and less expressive because you need to instantiate the resulting collection separately, and goes against the guidelines listed in the documentation.

Methods forEach() and forEachOrdered() exist as a last resort, and it’s discouraged to use them as a substitution of reduction operations like collect() or reduce(), for more information refer to the API documentation, pay attention to the code examples.

That’s how it would look like if we make use of collect():

Map<Integer, Long> sortedCards = cards.stream()
    .collect(Collectors.groupingBy(card -> card.kind.rank, Collectors.counting()))
    .entrySet().stream()
    .sorted(Map.Entry.<Integer, Long>comparingByValue(Comparator.reverseOrder())
        .thenComparing(Map.Entry::getKey))
    .collect(Collectors.toMap(
        Map.Entry::getKey,
        Map.Entry::getValue
    ));

Leave a Reply