2

I try to write following code:

Map<String, String> m = new HashMap<>();
// ... populate the map
m.entrySet().stream()
      .sorted(Comparator.comparing(e -> e.getKey()).reversed())
      .forEach(System.out::println);

This does not compile, because the inferred type for e is Object. However, if I remove the .reversed() call, the inference works. I have to spell out the type for e, which is ugly:

      .sorted(Comparator.comparing((Map.Entry<String, String> e) -> e.getKey()).reversed())

Compiler can infer the type for Comparator.comparing, but when I add reversed(), it cannot. Why?

Oliv
  • 10,221
  • 3
  • 55
  • 76

2 Answers2

1

This answer explains why it doesn't compile.

You have several ways to fix the code:

//cheat a bit
.sorted(Entry.comparingByKey(Comparator.reverseOrder()))

//provide the target type
.sorted(Entry.<String, String> comparingByKey().reversed())

//explicitly give the type of e
.sorted(Comparator.comparing((Entry<String, String> e) -> e.getKey()).reversed())
Community
  • 1
  • 1
assylias
  • 321,522
  • 82
  • 660
  • 783
  • This are two alternative solutions. But neither is as elegant as the intuitive original one. Actually, I'm designing an API, that suffers from the same problem as the `sorted()` method. I wonder, if I can change something in the API to make the simple version work. – Oliv Feb 08 '17 at 10:16
  • @Oliv I don't think these are less elegant, just different - however I agree with you that actual answer would need to explain why your solution doesn't work – Deltharis Feb 08 '17 at 10:20
  • I mean, less elegant than `.sorted(Comparator.comparing(e -> e.getKey()).reversed())` or even `.sorted(Comparator.comparing(Entry::getKey).reversed())` – Oliv Feb 08 '17 at 11:07
  • Neither do they allow further chaining, such as with `.thenComparing(...)` – Oliv Feb 08 '17 at 11:12
  • @Oliv I've added more information. Note that if you want to use `thenComparing()` you will need to use the same tricks in the second comparator. – assylias Feb 08 '17 at 11:50
  • @assylias, thanks for the reference to answer. It really is a compiler misfeature. – Oliv Feb 08 '17 at 11:57
  • `.sorted(Collections.reverseOrder(Comparator.comparing(e -> e.getKey())))` or `.sorted(Collections.reverseOrder(Map.Entry.comparingByKey()))` will work too – Holger Feb 08 '17 at 20:02
0

The static method you are using here

Comparator.comparing()

is a generic method, but you do not specify here, what it takes as an argument, and what it returns - it needs to be specified somewhere.

My suggestion is a small change, replacing casting e to Map.Entry, that could potentially look better

Map<String, String> m = new HashMap<>();
// ... populate the map
m.entrySet().stream()
        .sorted(Comparator.<Map.Entry<String, String>, String>comparing(Map.Entry::getKey).reversed())
        .forEach(System.out::println);
Oliv
  • 10,221
  • 3
  • 55
  • 76
amalgamat
  • 130
  • 2
  • 12
  • Normally, Java infers the type arguments. But after adding `reverser()`, I need to spell them explicitly. Why? – Oliv Feb 08 '17 at 10:14
  • I made some tests and when you have code without `reversed()`, the `Map.Entry::getKey` is resolved as `(stringStringEntry) -> stringStringEntry.getKey()`, but in case when you have it reversed, the same `Map.Entry::getKey` is resolved as `(Function) (t) -> Map.Entry.getKey(t)` which is clearly wrong, as now we would have `Comparator` returned as well as undefined type `U` and wrong `getKey` method call as it takes `t` as a parameter and is called on `Map.Entry` class. Very interesting, but still not understandable for me. – amalgamat Feb 08 '17 at 13:03