Java Lambda Method Reference: Complete Guide with Examples
In methods requiring functional parameters, you can directly pass another method of the same functional type—this is called method reference binding, similar to function pointers in C. Lambda expressions can replace method references; alternatively, method references are a special case of lambdas that do not allow control over passed arguments.
Constructor Reference
private Employee createEmployee(Supplier<Employee> supplier) {
return supplier.get();
}
@Test
public void testConstructorReference() {
Employee emp = createEmployee(Employee::new);
System.out.println(emp);
}
Requires a no-argument constructor.
Static Method Reference
private static void log(String message) {
System.out.println(message);
}
@Test
public void testStaticReference() {
Arrays.asList("aa", "bb", "cc").forEach(MethodRefDemo::log);
}
Simple: ensure the static method’s parameter list matches the required Functional Interface (FI) parameters.
Instance Method Reference
@Test
public void testInstanceMethodReference() {
Arrays.asList("aa", "bb", "cc").forEach(System.out::println);
}
Simple: ensure the instance method’s parameter list matches the required FI parameters.
Arbitrary Object’s Instance Method Reference (Tricky)
@Test
public void testArbitraryObjectReference() {
String[] strings = {"zzaa", "xxbb", "yycc"};
Arrays.sort(strings, String::compareToIgnoreCase);
System.out.println(Arrays.asList(strings));
File[] hiddenFiles = new File("C:").listFiles(File::isHidden);
}
Consider itnernal classes:
@Data
public static class Employee {
private String name;
private Integer age;
public int compareByAge(Employee other) {
return other.getAge() - this.getAge();
}
public void print() {
System.out.println(this);
}
public void printEmployee(Employee other) {
System.out.println(other);
}
public int compareTwoEmployees(Employee emp1, Employee emp2) {
return emp1.getAge().compareTo(emp2.getAge());
}
}
public static class AnotherEmployee {
public void print() {
System.out.println(this);
}
public void printEmployee(Employee other) {
System.out.println(other);
}
}
Test code:
@Test
public void testArbitraryObjectReference2() {
Function<String, String> toUpper1 = String::toUpperCase;
UnaryOperator<String> toUpper2 = x -> x.toUpperCase();
class Printer {
public void printMessage(String s) {
System.out.println(s);
}
}
Consumer<String> printer1 = new Printer()::printMessage;
Consumer<String> printer2 = x -> new Printer().printMessage(x);
Employee[] employees = {new Employee(), new Employee()};
Arrays.sort(employees, Employee::compareByAge);
Arrays.sort(employees, (x, y) -> x.compareByAge(y));
Arrays.sort(employees, new Employee()::compareTwoEmployees);
Arrays.sort(employees, (x, y) -> new Employee().compareTwoEmployees(x, y));
Comparator<Employee> comparator1 = new Employee()::compareTwoEmployees;
Comparator<Employee> comparator2 = Employee::compareByAge;
}
Summary:
- Static method reference: if the passed method is a type’s static method with matching parameters.
- Instance method reference: if the passed method is an instance’s member method with matching parameters.
- Arbitrary object’s instance method reference: if the passed method is a member method of type T with N-1 parameters (matching the FI’s required N parameters), the omitted parameter must be of type T (and the method reference prefix must be T’s class name).
Analysis of Omitted Parameter
Define a FI:
public interface CustomFunctionalInterface {
void process(TestBeanA beanA, TestBeanB beanB);
}
TestBeanA:
public class TestBeanA {
public void handleA(TestBeanA bean) {}
public void handleB(TestBeanB bean) {}
public void execute(CustomFunctionalInterface fi) {}
}
TestBeanB:
public class TestBeanB {
public void handleA(TestBeanA bean) {}
public void handleB(TestBeanB bean) {}
public void execute(CustomFunctionalInterface fi) {}
}
Test code:
public class MethodRefDemo {
public static void main(String[] args) {
TestBeanA beanA = new TestBeanA();
beanA.execute(TestBeanA::handleB); // OK
beanA.execute(TestBean2::handleA); // Error
}
}
Key observation: For a FI method with (T1, T2) parameters, only the first parameter (T1) can be omitted. The method reference prefix must be T1’s class name, which the lambda automatically instantiates as the omitted parameter.