Replace verbose lambdas with concise method references using ::, covering all four reference types.
Published February 20, 2025
Method references are a shorthand for lambda expressions that do nothing but call an existing method. They make code more readable when the lambda body is a direct method call.
1. Static method reference — ClassName::staticMethod
// Lambda
Function<String, Integer> parser = s -> Integer.parseInt(s);
// Method reference
Function<String, Integer> parser = Integer::parseInt;
// Usage
List<Integer> numbers = List.of("1", "2", "3").stream()
.map(Integer::parseInt)
.toList();
2. Instance method of a particular object — instance::method
String prefix = "Hello, ";
Function<String, String> greeter = s -> prefix.concat(s);
// Method reference to a specific instance
Function<String, String> greeter = prefix::concat;
// Usage with a specific logger
Consumer<String> logger = System.out::println;
List.of("a", "b", "c").forEach(System.out::println);
3. Instance method of an arbitrary instance — ClassName::instanceMethod
The first argument to the lambda becomes this.
// Lambda: str is the instance, trim() is called on it
Function<String, String> trimmer = str -> str.trim();
// Method reference
Function<String, String> trimmer = String::trim;
// More examples
Predicate<String> isEmpty = String::isEmpty;
Comparator<String> comp = String::compareTo;
// In streams
List<String> trimmed = names.stream()
.map(String::trim)
.filter(Predicate.not(String::isEmpty))
.toList();
4. Constructor reference — ClassName::new
// Lambda
Supplier<ArrayList<String>> factory = () -> new ArrayList<>();
// Constructor reference
Supplier<ArrayList<String>> factory = ArrayList::new;
// With arguments
Function<String, StringBuilder> sbFactory = StringBuilder::new;
// Copying a list
List<String> copy = original.stream()
.collect(Collectors.toCollection(ArrayList::new));
List<Person> people = ...;
// Extracting fields
List<String> names = people.stream()
.map(Person::getName) // instance method reference
.sorted(String::compareTo) // comparator
.toList();
// Sorting
people.sort(Comparator.comparing(Person::getName)
.thenComparing(Person::getAge));
// Predicate composition
Predicate<String> isEmail = String::contains; // needs a parameter — this won't work
// Use lambdas when arguments are needed:
Predicate<String> isEmail = s -> s.contains("@");
// Event handlers
button.addActionListener(this::handleClick);
// Collectors
Map<String, List<Person>> byCity = people.stream()
.collect(Collectors.groupingBy(Person::getCity));
| Situation | Prefer |
|---|---|
| Lambda body = single method call | Method reference |
| Need to transform arguments | Lambda |
| Need multiple statements | Lambda |
| Readability is unclear | Lambda |
ArrayList::new) are commonly seen in Collectors.toCollection().Person::getName is the most common pattern — an instance method of an arbitrary object.