4

Suppose I have a stream of Strings:

Stream<String> stream = Stream.of("c","a","b");

This works OK:

stream.sorted(Comparator.reverseOrder())

But using alternative syntax doesn't:

stream.sorted(Comparator::reverseOrder)
                        ^
   Bad return type in method reference: cannot convert Comparator<T> to int

When I Ctrl+Left Click in my IDE both take me to the same static method returning a Comparator in Comparator class:

  public static <T extends Comparable<? super T>> Comparator<T> reverseOrder() {
        return Collections.reverseOrder();
    }

One book explains:

Comparator is a functional interface. This means that we can use method references or lambdas to implement it. The Comparator interface implements one method that takes two String parameters and returns an int . However, Comparator::reverseOrder doesn’t do that. It is a reference to a function that takes zero parameters and returns a Comparator . This is not compatible with the interface. This means that we have to use a method and not a method reference.

But I don't understand it.

Andrejs
  • 10,803
  • 4
  • 43
  • 48
  • 4
    I do not understand your confusion, reverseOrder() is a method that returns a Comparator with *some* behaviour. `sorted` expects one such comparator, **not** a method-reference to get some Comparator. - `Comparator::reverseOrder` is **not** "alternative syntax" for `Comparator.reverseOrder()`. – luk2302 Aug 15 '17 at 20:14
  • @luk2302 's comment above seems like the answer you are looking for. `Comparator.reverseOrder()` is a method call that returns a `Comparator`. `Comparator::reverseOrder` is a reference to a function. A `Comparator` is itself a function that returns an `int`. Thus the error that states it sees a `Comparator` (returned by its call to `Comparator::reverseOrder`) where it expects to see an `int` (which would have been returned by its call **to the comparator** created by `Comparator.reverseOrder()` – MyStackRunnethOver Aug 15 '17 at 20:31
  • @luk2302 I would post your comment as an answer – MyStackRunnethOver Aug 15 '17 at 20:33
  • I would be happy to accept an answer that is a mix/merge of luk2302's and mystackrunnethover's – Andrejs Aug 15 '17 at 20:34

4 Answers4

3

Method references are equivalent to Lambda expressions that only can be used where a FunctionalInterface is expected. sorted doesn't expect a FunctionalInterface, it expects a plain, 'ol Comparator. If the signature of sorted were as follows, method reference would've worked: <T> sorted(Supplier<? extends Comparator<T>> s).

Abhijit Sarkar
  • 21,927
  • 20
  • 110
  • 219
  • Upvoting as well for "if the signature were". Thanks. For me, the complete and exhaustive answer is split across Grzegorz's, yours and darcula's answers. – Andrejs Aug 16 '17 at 08:26
2

Simply put, you need a Comparator and not a reference to a method returning one. Method references would work with any method that would match a Comparator's signature.

The actual alternative to

stream.sorted(Comparator.reverseOrder())

is

stream.sorted((o1, o2) -> Comparator.<String>reverseOrder().compare(o1, o2));

since the sorted method accepts a Comparator<? super T> instance, the Comparator::reverseOrder can't be used here because it's an instance of Supplier<Comparator<String>>.

Grzegorz Piwowarek
  • 13,172
  • 8
  • 62
  • 93
1

You can't use a lambda expression for a functional interface, if the method in the functional interface has type parameters.

Refer this answer - https://stackoverflow.com/a/22588738/8202194

To verify, try this -

class MyComparator implements Comparator<String> {

    @Override
    public int compare(String o1, String o2) {
        return o1.compareTo(o2);
    }
}

MyComparator myComparator = new MyComparator();
stream.sorted(myComparator::compare).forEach(s ->System.out.println(s));

This works because compare method of MyComparator knows the type - String in this case.

darcula
  • 94
  • 5
0

When a Comparator works in java it compares two values and returns either -1 for smaller, 0 for equal, and 1 for larger. The method reference is returning a type Comparator but the Collections reverse order method needs an int to know whether the elements being compared are smaller or larger so it can sort.

For example when it compares an array [c,a,b,c] First it'll compare c to a (by default it uses natural order to compare) so it would return 1 which means c is greater than a. When it compares a to b it would return a -1 which means a is less than b

Using these return values is how the Comparator sorts using the default .sort() provided in the interface

mep
  • 431
  • 1
  • 4
  • 15