What is the type of Map.Entry.comparingByValue().reversed()?

Viewed 10852

I have a list of map entries

Map<String, Integer> map = new HashMap<>();
...(fill the map)...
List<Entry<String, Integer>> entries = new ArrayList<>(map.entrySet());

and I want to sort it by values according to this answer, but in reverse order. When I do

Comparator<Entry<String, Integer>> cmp = Entry.comparingByValue();
entries.sort(cmp.reversed());

everything works fine. But when I try to shorten the above two lines to

entries.sort(Entry.comparingByValue().reversed());

the compiler returns

error: incompatible types: Comparator<Entry<Object,V>> cannot be converted to Comparator<? super Entry<String,Integer>>
        entries.sort(Entry.comparingByValue().reversed());
  where V is a type-variable:
    V extends Comparable<? super V>

This seems strange because the implementation of Comparator's reversed() default method simply transfers it to Collections.reverseOrder() and I can change the above line to

entries.sort(Collections.reverseOrder(Entry.comparingByValue()));

to work. But what is the correct type of Entry.comparingByValue().reversed()?

Comparator<Entry<String, Integer>> cmp = Entry.comparingByValue().reversed();

doesn't seem to work. Omitting the type parameter Entry<String, Integer> works, but this results in "unchecked method invocation" warning from the compiler later.

2 Answers

The compiler is not clever enough to infer the generic type parameters in this case. You can specify them explicitly:

entries.sort(Entry.<String, Integer>comparingByValue().reversed());

The issue is that you first need the type of the comparator returned by comparingByValue to know the type of the one returned by reversed.

I don't see any good reason why a clever compiler couldn't work backwards and know that sort requires a particular type, so reversed must be a particular type, so comparingByValue must be a particular type. But that's not how things are.

Type inference is still being worked on (as recently as Java 10), so maybe in the future, who knows.

When you shorten down

Comparator<Entry<String, Integer>> cmp = Entry.comparingByValue();
entries.sort(cmp.reversed());

to

entries.sort(Entry.comparingByValue().reversed());

you remove the type information gleaned from cmp’s declaration. The compiler still needs to know that information, so you need to add the generic typing to Entry:

entries.sort(Entry.<String, Integer>comparingByValue().reversed());
Related