13

I'm trying to make sense of how Comparator.comparing function works. I created my own comparing method to understand it.

private static <T,U extends Comparable<U>> Comparator<T> comparing(Function<T,U> f) {
    BiFunction<T,T,Integer> bfun = (T a, T b) -> f.apply(a).compareTo(f.apply(b));
    return (Comparator<T>) bfun;
}

The last line in this function throws an exception.

However, if I change this function to

private static <T,U extends Comparable<U>> Comparator<T> comparing(Function<T,U> f) {
    return (T a, T b) -> f.apply(a).compareTo(f.apply(b));
}

It works just fine as expected.

What is the intermediate functional interface which the second attempt uses, which is able to convert the lambda to Comparator?

Andrew Tobilko
  • 48,120
  • 14
  • 91
  • 142
patentfox
  • 1,436
  • 2
  • 13
  • 28
  • 4
    The type that a lambda converts to is the type the expression must be from its context. In the first example the lambda must convert to a `BiFunction` because that's the type of the variable it's assigned to. In the second example the lambda must convert to a `Comparator` because that's the return type of the method. `BiFunction` and `Comparator` have the same "shape", so the same lambda can become either depending on context, but they are different types so casting one to another will fail. – Reinstate Monica Nov 03 '17 at 19:37
  • I'm downvoting this. Not for quality, but because there were a number of audits on this question, out of which *three* were failed. – Nissa Nov 16 '17 at 19:22

2 Answers2

12

What is the intermediate functional interface which the second attempt uses, which is able to convert the lambda to Comparator?

The Comparator itself.

Within the second method, you have defined a Comparator, not an intermediate object that has been cast to the Comparator.

The last line in this function throws an exception.

Yes, it should.

If two classes are functional interfaces and have similar methods (with the identical signatures and the same return type), it doesn't mean that they can be used interchangeably.


An interesting trick - you may make a Comparator<T> by referring to the BiFunction<T, T, Integer> bfun's method apply:

private static <T,U extends Comparable<U>> Comparator<T> comparing(Function<T,U> f) {
    final BiFunction<T,T,Integer> bfun = (T a, T b) -> f.apply(a).compareTo(f.apply(b));
    return bfun::apply; // (a, b) -> bfun.apply(a, b);
}
Andrew Tobilko
  • 48,120
  • 14
  • 91
  • 142
6

The intermediate functional interface in your second attempt is simply Comparator<T>:

You can see this because your code-snippet is equivalent to the following:

private static <T,U extends Comparable<U>> Comparator<T> comparing(Function<T,U> f) {
    Comparator<T> comparator = (T a, T b) -> f.apply(a).compareTo(f.apply(b));
    return comparator;
}
Thomas Fritsch
  • 9,639
  • 33
  • 37
  • 49