Method References in Java
Introduction
Introduced in Java 8 alongside Lambda Expressions, Method References provide a shorthand syntax to create a lambda expression from an existing method. They make your code more concise, readable, and functional.
When a lambda expression does nothing but call an existing method, a method reference can be used to replace the lambda expression entirely, utilizing the double colon :: operator.
Syntax and Comparison
Imagine you have a list of names and you want to print each one.
Using an Anonymous Inner Class:
names.forEach(new Consumer<String>() {
@Override
public void accept(String name) {
System.out.println(name);
}
});
Using a Lambda Expression:
names.forEach(name -> System.out.println(name));
Using a Method Reference:
names.forEach(System.out::println);
In the method reference, System.out is the target object, and println is the method.
The Four Types of Method References
Java defines four distinct types of method references, depending on the nature of the method being called and the object it is called upon.
1. Reference to a Static Method
Syntax: ContainingClass::staticMethodName
When the lambda calls a static method, you can reference the class name directly.
// Lambda
Function<String, Integer> parserLambda = s -> Integer.parseInt(s);
// Method Reference
Function<String, Integer> parserRef = Integer::parseInt;
System.out.println(parserRef.apply("123")); // Outputs 123
2. Reference to an Instance Method of a Particular Object
Syntax: containingObject::instanceMethodName
Used when you are calling an instance method on a specific, already-existing object.
String prefix = "Hello, ";
// Lambda
Function<String, String> greetLambda = name -> prefix.concat(name);
// Method Reference
Function<String, String> greetRef = prefix::concat;
System.out.println(greetRef.apply("World")); // Outputs "Hello, World"
3. Reference to an Instance Method of an Arbitrary Object of a Particular Type
Syntax: ContainingType::methodName
This is often the most confusing type. It is used when the method is an instance method, but the target object is supplied as the first parameter of the lambda.
// Lambda
BiPredicate<String, String> equalsLambda = (s1, s2) -> s1.equals(s2);
// Method Reference
BiPredicate<String, String> equalsRef = String::equals;
// Lambda
Function<String, String> lowerLambda = s -> s.toLowerCase();
// Method Reference
Function<String, String> lowerRef = String::toLowerCase;
Here, the method toLowerCase() is called on the argument s provided at runtime.
4. Reference to a Constructor
Syntax: ClassName::new
Used to create new object instances, replacing lambdas that just instantiate objects.
// Lambda
Supplier<List<String>> listSupplierLambda = () -> new ArrayList<>();
// Method Reference
Supplier<List<String>> listSupplierRef = ArrayList::new;
List<String> list = listSupplierRef.get();
When NOT to Use Method References
While method references make code cleaner, they should be avoided if they reduce readability.
- If your lambda expression modifies arguments, performs calculations, or handles multiple statements, you cannot use a simple method reference.
- If the method reference introduces ambiguity (e.g., overloaded methods where the compiler struggles to infer the target type), a lambda expression is safer and clearer.
Summary
- Method references use the
::operator as a shorthand for lambdas that only invoke a single method. - There are four types: Static, Specific Instance, Arbitrary Instance of a Type, and Constructor.
- They enhance readability and embody the declarative style of Java's Stream API and Functional Interfaces.