4

I'm using Comparators to sort streams and I've come across a compiler error that I don't understand.

Let's say I have the following classes:

class Base {
    private LocalDate date;
    public LocalDate getDate() { return date; }
    ...
}
class Sub extends Base { ... }

I'm creating two comparators to sort Subs by their date, one by the natural order and one in the reverse order. The following code compiles:

Comparator<Sub> fwd = Comparator.comparing(Sub::getStartDate);
Comparator<Sub> rev1 = Comparator.comparing(Sub::getStartDate).reversed();
Comparator<Sub> rev2 = fwd.reversed();

Realising that getDate() is defined on Base, I thought I'd try the following:

Comparator<Sub> fwd = Comparator.comparing(Base::getStartDate);
Comparator<Sub> rev1 = Comparator.comparing(Base::getStartDate).reversed();  // Compiler error
Comparator<Sub> rev2 = fwd.reversed();  // OK

Surprisingly (to me), the code for rev2 compiles okay, while the code for rev1 produces the following errors:

Cannot infer type argument(s) for <T, U> comparing(Function<? super T, ? extends U>)
The type Base does not define getStartDate(Object) that is applicable here

Why do I get these compiler errors? And why can I effectively circumvent them when building rev2 from fwd?

(I'm using Eclipse Oxygen.3a (4.7.3) and Java v1.8.0_162 if that's relevant.)

dave
  • 11,641
  • 5
  • 47
  • 65
  • You probably just need to explicitly state the generic type argument on the `comparing` method. – Louis Wasserman Jun 10 '18 at 00:42
  • @aominè I'm not convinced this is a duplicate of the question you refer to. In that question there is no parent/child class relationship. Further, in that question, using a method reference compiles, while in mine it does not. Can you explain why you think the other question's answer explains my situation. – dave Jun 10 '18 at 00:57
  • 1
    @dave post reopened. – Ousmane D. Jun 10 '18 at 08:18
  • Where is the class `Competition` in your explanation? – Yassin Hajaj Jun 10 '18 at 13:35
  • @YassinHajaj The reference to `Competition` was a mistake and came from my actual problem as opposed to the [mcve](https://stackoverflow.com/help/mcve) in my question. I've edited my question to address this. – dave Jun 11 '18 at 02:10

1 Answers1

2

As stated it this answer, the target typing is disrupted by the presence of the call to reversed():

Comparator<Sub> rev1 =
    Comparator.comparing(Base::getStartDate)
              .reversed(); // Compiler error - incompatible types

It seems like in this situation compiler infers the following type arguments:

Comparator<Sub> rev1 =
    Comparator.<Base, LocalDate>comparing(Base::getStartDate)
              .reversed(); // Compiler error - incompatible types

To get rid of the error, you can provide type arguments explicitly:

Comparator<Sub> rev1 =
    Comparator.<Sub, LocalDate>comparing(Base::getStartDate)
              .reversed(); // Okay, no problem

Or use two-argument Comparator.comparing():

Comparator<Sub> rev1 =
    Comparator.comparing(Base::getStartDate, Comparator.reverseOrder());
Oleksandr Pyrohov
  • 14,685
  • 6
  • 61
  • 90
  • 1
    Thanks for the explanation. I tested your first solution and it resolved the error which suggests your supposition re type inference is correct. I had discovered your second solution already and have retained it as it seems clearer (and easier on the eye). – dave Jun 11 '18 at 04:04