Method References
A method reference is a shorter and more readable way to represent a lambda expression when the lambda simply calls an existing method. Instead of writing the complete lambda body, Java allows you to refer directly to the method using the :: operator.
Method references do not introduce a new kind of functional programming behavior. They are simply a cleaner syntax for certain lambda expressions. The compiler determines the required functional interface from the surrounding context.
Why Method References Exist
Consider a simple lambda that prints every name in a list.
names.forEach(name -> System.out.println(name));
The lambda receives name and immediately passes it to System.out.println(). Since the lambda does nothing except call an existing method, it can be shortened to a method reference.
names.forEach(System.out::println);
Both versions perform the same operation. The second version communicates the intention more directly: "use System.out.println for each element."
Remember: A method reference is not a method call. It is a reference to an existing method that can be used where a compatible functional interface is expected.
Method Reference Syntax
ClassName::methodName
The exact form depends on the kind of method being referenced. Java supports four main forms.
| Type | Syntax | Typical Example |
|---|---|---|
| Static method | ClassName::staticMethod | Math::abs |
| Instance method of a particular object | object::instanceMethod | System.out::println |
| Instance method of an arbitrary object of a type | ClassName::instanceMethod | String::toUpperCase |
| Constructor | ClassName::new | ArrayList::new |
Method Reference with Static Methods
A static method belongs to the class rather than a particular object. A static method reference uses the class name followed by :: and the method name.
import java.util.function.Function;
public class Main {
static int square(int number) {
return number * number;
}
public static void main(String[] args) {
Function<Integer, Integer> operation =
Main::square;
System.out.println(operation.apply(5));
}
}
The method reference Main::square can be used because the method accepts one Integer-compatible argument and returns an Integer-compatible result, matching the Function.
Lambda vs Static Method Reference
Function<Integer, Integer> first =
number -> square(number);
Function<Integer, Integer> second =
Main::square;
When the lambda only forwards its argument to an existing compatible method, the method reference is often clearer.
Referencing Existing Java Methods
You can reference methods provided by standard Java classes.
import java.util.function.Function;
Function<Integer, Integer> absolute =
Math::abs;
System.out.println(absolute.apply(-25));
Here, Math::abs refers to the existing abs() method. The Function supplies the input when apply() is called.
Method Reference with a Particular Object
A method reference can point to an instance method of a specific object.
import java.util.function.Consumer;
public class Main {
public static void main(String[] args) {
Consumer<String> printer =
System.out::println;
printer.accept("Hello Java");
}
}
Here, System.out is a particular object, and println is its instance method.
Lambda vs Object Method Reference
Consumer<String> first =
text -> System.out.println(text);
Consumer<String> second =
System.out::println;
The two forms are equivalent for this use case. The method reference removes unnecessary parameter and method-call syntax.
Method Reference with an Arbitrary Object
One of the most interesting forms uses a class name to reference an instance method. In this case, the object on which the method will be called is supplied by the functional interface.
import java.util.function.Function;
Function<String, String> upperCase =
String::toUpperCase;
System.out.println(
upperCase.apply("java")
);
The method reference is equivalent to the following lambda:
Function<String, String> upperCase =
text -> text.toUpperCase();
The String object supplied to apply() becomes the object on which toUpperCase() is invoked.
Method Reference with String Methods
import java.util.*;
import java.util.function.Function;
public class Main {
public static void main(String[] args) {
List<String> names =
Arrays.asList("Ravi", "Anita", "Priya");
names.stream()
.map(String::toUpperCase)
.forEach(System.out::println);
}
}
The first method reference transforms each String. The second method reference prints each resulting String. This creates a clean stream pipeline without unnecessary lambda syntax.
Method Reference in Sorting
Method references are also useful when sorting objects using an existing accessor method.
import java.util.*;
class Student {
String name;
Student(String name) {
this.name = name;
}
String getName() {
return name;
}
}
public class Main {
public static void main(String[] args) {
List<Student> students = Arrays.asList(
new Student("Ravi"),
new Student("Anita"),
new Student("Priya")
);
students.sort(
Comparator.comparing(Student::getName)
);
}
}
The method reference tells Comparator.comparing() which method should be used to obtain the comparison value.
Method Reference with Multiple Parameters
A method reference can also work with methods that accept multiple parameters, provided the functional interface has a compatible method signature.
import java.util.function.BiFunction;
public class Main {
public static void main(String[] args) {
BiFunction<Integer, Integer, Integer> maximum =
Math::max;
System.out.println(maximum.apply(20, 35));
}
}
The Math.max() method accepts two values and returns one value, matching the shape expected by BiFunction.
Method Reference and Functional Interfaces
A method reference does not have a standalone type by itself. It needs a target functional interface to determine how the referenced method should be used.
Function<String, Integer> length =
String::length;
System.out.println(length.apply("Java"));
The compiler understands that String::length must behave like a Function because the variable expects Function<String, Integer>.
A useful mental model is: the functional interface provides the "shape," while the method reference provides the existing implementation.
When Lambda Is Better
Method references are useful, but they should not be used simply because they are shorter. Sometimes a lambda communicates the intention more clearly.
names.forEach(
name -> System.out.println("Student: " + name)
);
Trying to force this into a method reference would not necessarily make the code clearer because the lambda adds useful context and formatting logic.
When Method Reference Is Better
A method reference is usually a strong choice when the lambda only calls an existing method without adding any additional logic.
numbers.forEach(number -> System.out.println(number));
This can be simplified to:
numbers.forEach(System.out::println);
The shorter version is easier to scan and immediately communicates that every value should be printed.
Common Beginner Mistakes
- Confusing :: with the normal method-call operator ().
- Assuming every lambda can automatically be converted into a method reference.
- Ignoring the required functional interface signature.
- Using a method reference when a lambda would communicate additional logic more clearly.
- Confusing a reference to an instance method with a reference to a static method.
Best Practices
- Use method references when they make the code simpler and easier to understand.
- Prefer them when a lambda only forwards its arguments to an existing method.
- Keep the target functional interface obvious from the surrounding code.
- Do not sacrifice readability merely to reduce the number of characters.
- Understand the four method-reference forms before using them extensively.
Interview Insight
Interview shortcut: Method references use the :: operator to refer to existing methods or constructors. The four common forms are static method references, instance methods of a particular object, instance methods of an arbitrary object of a type, and constructor references.
Quick Revision
| Form | Syntax | Example |
|---|---|---|
| Static method | Class::method | Math::abs |
| Particular object | object::method | System.out::println |
| Arbitrary object | Class::instanceMethod | String::toUpperCase |
| Constructor | Class::new | ArrayList::new |
| Operator | :: | String::length |
Final Takeaway
Method references provide a clean bridge between existing object-oriented Java methods and functional programming. Whenever a lambda simply calls an existing compatible method, the :: syntax can often make the code shorter and more expressive. The key is not to use method references everywhere, but to use them where they make the programmer's intention immediately obvious.
