5

How exactly do you chain method references for instances with Java 8? Example:

Collections.sort(civs,Comparator.comparing(Civilization::getStrategy.getStrategLevel));

getStrategy of a Civilization instance returns a Strategy object instance which has the instance method getStrategyLevel.

Why doesn't the Comparator.comparing method return a comparator with it's functional interface implemented by the lambda expression?

Andrew Tobilko
  • 48,120
  • 14
  • 91
  • 142
Nemo_Sol
  • 352
  • 1
  • 3
  • 17
  • 4
    Just use a lambda. – Nándor Előd Fekete Nov 05 '16 at 16:28
  • 2
    A method reference is a _reference to a method_. The chain "call `getStrategy()`, then call `getStrategyLevel()` on the result) is not a method, therefore it is not suitable for describing with a method reference. If you want to describe an arbitrary bag of imperative code, use a lambda; method refs are a (hopefully more readable) shorthand provided for the special case where the lambda body is a single method whose argument list matches exactly that of the target functional interface. – Brian Goetz Nov 06 '16 at 18:31
  • See also: http://stackoverflow.com/questions/24487805/lambda-expression-vs-method-reference/24493905#24493905 – Brian Goetz Nov 06 '16 at 18:31

4 Answers4

4

In that case, you should use a lambda, you can't apply a method reference directly:

Collections.sort(civs, Collectors.comparing(c -> c.getStrategy().getStrategLevel()));

Though, there is a way to use a method reference here. Assuming that you have a class like

class CivilizationUtils {
    public static Integer getKeyExtractor(Civilization c) {
        return c.getStrategy().getStrategLevel();
    }
}

the issue could be solved like

Collections.sort(civs, Collectors.comparing(CivilizationUtils::getKeyExtractor));
Andrew Tobilko
  • 48,120
  • 14
  • 91
  • 142
  • getStrategy is not a static method – Nemo_Sol Nov 05 '16 at 16:31
  • how would I provide a method reference to the instance method `getStrategyLevel` through an instance of `Strategy` which is accessed through an instance of `Civilization`. Should I just use a regular lambda expression for the comparator? – Nemo_Sol Nov 05 '16 at 16:36
3

You cannot do it with a method reference, you need to use a lambda expression or create a static method.

There are four kinds of method references:

  1. Reference to a static method like ContainingClass::staticMethodName
  2. Reference to an instance method of a particular object like containingObject::instanceMethodName
  3. Reference to an instance method of an arbitrary object of a particular type like ContainingType::methodName
  4. Reference to a constructor like ClassName::new

More details about method reference.

So here, with a lambda expression it would be:

Collections.sort(civs, Comparator.comparing(c -> c.getStrategy.getStrategLevel()));

Or in case you create a static method

public static int getStrategLevel(Civilization c) {
    return c.getStrategy().getStrategLevel();
}

Then your code would be:

Collections.sort(civs, Comparator.comparing(MyClass::getStrategLevel));
Nicolas Filotto
  • 43,537
  • 11
  • 94
  • 122
1
Collections.sort(civs,Comparator.comparing(civ -> civ.getStrategy().getStrategLevel()));
Shashank
  • 416
  • 5
  • 16
0

Different example, but I have a method

void m(Predicate<String> stringPredicate)

and a utility class

class Utilities {
  static boolean condition1(String s) { ... }
  static boolean condition2(String s) { ... }
  ...
}

and I wanted to invoke m with a predicate that returns true iff Utilities.condition1 returns false. The Java grammar allows me to write

m(Utilities::condition1)

but not

m(Utilities::condition1.negate())

(an unfortunate violation of referential transparency), and the compiler complained, "Java Method reference not expected here."

My workaround was to write a method

Predicate<String> not(Predicate<String> p) {
  return p;
}

and then to write the call

m(not(Utilities::condition1))

--which is allowed by the Java grammar.

nhcohen
  • 151
  • 1
  • 5