Q26 of 40 · Core Java
What is a method reference and how does it relate to a lambda?
Short answer
Short answer: A method reference is shorthand for a lambda that does nothing but call an existing method. `String::toUpperCase` is equivalent to `s -> s.toUpperCase()`. There are four kinds: static method, instance method of an arbitrary instance, instance method of a particular instance, and constructor.
Detail
When a lambda does nothing but delegate to an existing method — s -> s.toUpperCase(), obj -> obj.toString(), n -> Integer.parseInt(n) — a method reference expresses the same thing more concisely. The compiler resolves the method reference to the same functional interface as the lambda would target.
Four kinds:
Static method:
Integer::parseInt→n -> Integer.parseInt(n). The method is called on the class, not an instance.Instance method of an arbitrary instance (bound to type):
String::toUpperCase→s -> s.toUpperCase(). The first parameter of the lambda becomes the receiver.Instance method of a particular instance (bound to object):
System.out::println→x -> System.out.println(x). The method is always called on that specificSystem.outinstance.Constructor:
ArrayList::new→() -> new ArrayList<>(). Used withSupplier<T>orFunction<Integer, ArrayList>.
Method references are not always clearer — s -> s.toUpperCase() is actually very readable. Prefer method references when:
- The lambda body is a single call with no modification of arguments
- The method name adds semantic meaning (
User::getEmailtells you more thanu -> u.getEmail()) - You're using a well-known static method like
Objects::nonNull,String::isEmpty
In test automation: method references appear in stream pipelines (cases.stream().map(TestCase::id).toList()), assertion helpers, and as Comparator arguments (Comparator.comparing(TestCase::severity)).
// EXAMPLE
MethodReferences.java
import java.util.List;
import java.util.Objects;
List<String> tags = List.of("smoke", null, "regression", null, "e2e");
List<TestCase> cases = getTestCases();
// 1. Static method reference
// n -> Integer.parseInt(n)
List<Integer> ids = List.of("1", "2", "3").stream()
.map(Integer::parseInt) // static method ref
.toList();
// 2. Instance method of arbitrary instance (bound to type)
// s -> s.toUpperCase()
List<String> upper = tags.stream()
.filter(Objects::nonNull) // static: t -> Objects.nonNull(t)
.map(String::toUpperCase) // instance on receiver
.toList();
// 3. Instance method of particular instance
// x -> System.out.println(x)
tags.stream()
.filter(Objects::nonNull)
.forEach(System.out::println); // bound to System.out instance
// 4. Constructor reference
// () -> new ArrayList<>()
import java.util.ArrayList;
import java.util.function.Supplier;
Supplier<List<String>> listFactory = ArrayList::new;
List<String> fresh = listFactory.get();
// Comparator composed with method refs
cases.sort(
Comparator.comparing(TestCase::severity) // instance method ref
.thenComparing(TestCase::id)
);