Card 06/ 09

ConceptDifficulty: Advanced1 min

Method References, and When a Lambda Is Clearer

String::toUpperCase says the same as s -> s.toUpperCase(), until the body does anything more.

A method reference, written Type::method, is a shorter way to write a lambda whose body only calls one existing method and passes its parameters straight through. The compiler turns it into the same functional interface, an interface with a single abstract method, the lambda would have become.

The four forms of method reference, with the lambda each replaces
FormExampleThe lambda it replaces
A static methodInteger::parseInts -> Integer.parseInt(s)
A method on a particular objectSystem.out::printlns -> System.out.println(s)
A method on whichever object arrivesString::toUpperCases -> s.toUpperCase()
A constructorArrayList::new() -> new ArrayList<>()

The third row is the one that reads oddly at first. String::toUpperCase takes no argument in the source and produces a Function<String, String>: the parameter becomes the object the method is called on.

The other three rows read the way they look: the arguments are passed to a static method, to a method on an object you already hold, or to a constructor. In every case the compiler matches the referenced method against the one abstract method of the interface it is expecting.

You cannot use one where the body does anything other than pass its parameters straight through: reordering them, adding a constant, negating the result, or calling two things.

java
names.stream().map(String::toUpperCase)              // fine
names.stream().map(s -> s.toUpperCase().trim())      // two calls, so a lambda
names.stream().filter(s -> !s.isBlank())             // negated, so a lambda

Use a method reference when it fits and do not contort a lambda to make one fit. s -> !s.isBlank() is clearer than any arrangement that avoids it.