library
intermediateMethod References & Four Types
Use method references as concise alternatives to lambdas, master all four types, and know when lambdas are clearer.
Method references are shorthand for lambdas that do nothing but call an existing method. They read better, and the interview value is in knowing the four forms and the one that behaves differently from how it looks.
Method reference = giving someone your business card (the method) instead of writing down your phone number and explaining how to call (the lambda). Both reach you, but the card is more direct.
Key Concepts
1
Static reference: Integer::parseInt. Equivalent to s -> Integer.parseInt(s). The arguments of the
functional interface become the arguments of the method.
Integer::parseInts -> Integer.parseInt(s)
2
Reference to an instance of a particular object: System.out::println. The receiver is fixed at the
moment the reference is created — System.out is captured — and the lambda's arguments are passed to the
method. Because the receiver is captured, a reference created from a field captures that field's current
value, which is why capturing a mutable field can surprise you later.
System.out::printlnSystem.out
3
Reference to an instance method of an arbitrary object: String::toLowerCase. This is the form people
misread. There is no receiver yet; the first argument becomes the receiver. So
String::toLowerCase is s -> s.toLowerCase(), and String::compareTo is (a, b) -> a.compareTo(b) — an
arity that looks wrong until you see the shift.
String::toLowerCases -> s.toLowerCase()String::compareTo(a, b) -> a.compareTo(b)
4
Constructor reference: ArrayList::new. Equivalent to () -> new ArrayList<>(), and it adapts to the
constructor's arity, so String::new can be a Function<char[], String>. With arrays, int[]::new is a
IntFunction<int[]>, which is what toArray wants.
ArrayList::new() -> new ArrayList<>()String::newFunction<char[], String>int[]::new
5
They are not free of evaluation order. expensive().method() written as a bound reference evaluates
expensive() once, when the reference is created — not on each call. That is usually what you want and
occasionally a bug, and it is the difference between obj::method and () -> obj.method() when obj is
recomputed.
expensive().method()expensive()obj::method() -> obj.method()obj
6
Readability is the actual criterion. map(this::convert) is clearer than map(x -> convert(x)), and
map(User::getName) is clearer than the lambda. But a method reference that forces the reader to work out
which of the four forms is in play is worse than the explicit lambda, and choosing on those grounds is the
mature answer.
map(this::convert)map(x -> convert(x))map(User::getName)
7
What the interviewer is probing.1. "What are the four kinds?" Probing: recall with understanding. Stalls: naming two. Moves up:
static, bound instance, unbound instance, constructor — with an example of each.
8
2. "What does String::toLowerCase mean?" Probing: the unbound form. Stalls: "It calls
toLowerCase." Moves up: the first argument becomes the receiver: s -> s.toLowerCase().
String::toLowerCases -> s.toLowerCase()
9
3. "Bound or unbound — how do you tell?" Probing: the distinction. Stalls: guessing. Moves up:
instance::method captures a receiver; Type::method takes it from the first argument.
instance::methodType::method
10
4. "When is the receiver evaluated?" Probing: the subtle one. Stalls: "On each call." Moves up:
at reference creation for a bound reference — so a changing field is captured once.
11
5. "When would you prefer a lambda?" Probing: judgement. Stalls: "Never, references are cleaner."
Moves up: when the reference is ambiguous to read, or when you need to adapt arguments the reference
cannot.
12
The four forms13
1. Static
Integer::parseInt -> s -> Integer.parseInt(s)
Math::max -> (a,b) -> Math.max(a,b)14
2. Bound instance (receiver captured now)
System.out::println -> x -> System.out.println(x)
this::convert -> x -> this.convert(x)
logger::debug -> m -> logger.debug(m)15
3. Unbound instance (FIRST ARG becomes the receiver)
String::toLowerCase -> s -> s.toLowerCase()
String::compareTo -> (a,b) -> a.compareTo(b)
User::getName -> u -> u.getName()16
4. Constructor
ArrayList::new -> () -> new ArrayList<>()
String::new -> cs -> new String(cs)
int[]::new -> n -> new int[n]17
Arity shift that catches people18
Comparator<String> c = String::compareTo;
// compareTo takes ONE argument, Comparator needs TWO
// -> works, because the first argument is the receiver:
// (a, b) -> a.compareTo(b)19
Capture timing20
Bound: Foo f = a; Runnable r = f::run; // captures a NOW
f = b; // r still uses a
Lambda: Runnable r = () -> f.run(); // f must be
// effectively final