Method References in Java
Method references are a cleaner and more concise way to use an existing method as a lambda expression.
Consider:
products.forEach(
product -> System.out.println(product)
);The lambda isn’t adding any new logic. It simply forwards its parameter to an existing method:
System.out.println(product)We can remove this unnecessary layer using a method reference:
products.forEach(System.out::println);Both versions mean essentially the same thing:
Lambda
product -> System.out.println(product)
↓
Method Reference
System.out::printlnUse a method reference when your lambda simply calls an existing method.
Method Reference Syntax
Method references use the double-colon operator:
::The general idea is:
ClassOrObject :: methodNameFor constructors:
ClassName :: newThere are four types of method references worth knowing.
1. Reference to a Static Method
Suppose we already have:
static boolean isExpensive(Product product) {
return product.getPrice() >= 5.00;
}We could use a lambda:
products.removeIf(
product -> isExpensive(product)
);Or simply:
products.removeIf(MyClass::isExpensive);Pattern
ClassName::staticMethodVisualize it as:
product
│
▼
MyClass::isExpensive
│
▼
isExpensive(product)2. Reference to an Instance Method of a Specific Object
We’ve already seen this:
products.forEach(System.out::println);System.out refers to a specific PrintStream object.
The method reference means:
For every product
│
▼
System.out
│
▼
println(product)Pattern
objectReference::instanceMethodAnother example:
printer::printmeans that print() should be called on that particular printer object.
3. Reference to an Instance Method of Any Object of a Type
This one looks similar to a static method reference, but its behavior is different.
Suppose we want to extract product names:
products.stream()
.map(product -> product.getName())
.forEach(System.out::println);The lambda:
product -> product.getName()can become:
Product::getNameGiving us:
products.stream()
.map(Product::getName)
.forEach(System.out::println);But getName() is not static.
So what is happening?
The stream supplies each Product object, and Java invokes getName() on that object.
Product A ──→ getName() ──→ "Book"
Product B ──→ getName() ──→ "Laptop"
Product C ──→ getName() ──→ "Coffee"Pattern
ClassName::instanceMethodThink of it as:
obj -> obj.method()becoming:
ClassName::methodFor example:
product -> product.getName()becomes:
Product::getName4. Reference to a Constructor
Method references can also point to constructors.
Get Suraj’s stories in your inbox
Join Medium for free to get updates from this writer.
Suppose we have:
interface ProductFactory {
Product create(
Category category,
String name,
double price
);
}Instead of:
ProductFactory factory =
(category, name, price) ->
new Product(category, name, price);we can write:
ProductFactory factory = Product::new;Now:
factory.create(
Category.BOOK,
"Effective Java",
45.00
);ultimately invokes the matching Product constructor.
Pattern
ClassName::newConceptually:
ProductFactory.create(...)
│
▼
Product::new
│
▼
new Product(...)The Four Types — Quick Reference
A useful memory trick:
STATIC
Class::methodSPECIFIC OBJECT
object::methodANY OBJECT OF TYPE
Class::methodCONSTRUCTOR
Class::new
Notice that static methods and instance methods of an arbitrary object have the same-looking syntax. The method declaration and target functional interface determine what it means.
Method References and Functional Interfaces
Just like lambda expressions, method references don’t exist independently.
They need a target functional interface.
Consider:
products.forEach(System.out::println);forEach() expects:
Consumer<Product>whose abstract method is:
void accept(Product product);Meanwhile, println() can accept a Product/Object and return void.
Conceptually:
Consumer<Product>
│
│ requires
▼
void accept(Product)
│
│ compatible with
▼
System.out::println
│
▼
void println(Object)The signatures are compatible, so the method reference can be used.
A method reference must be compatible with the abstract method of its target functional interface.
Lambda vs Method Reference
Consider:
product -> System.out.println(product)If the lambda simply forwards its arguments:
Lambda
│
▼
calls existing method
│
▼
No additional logic?
│
YES
▼
Consider Method ReferenceSo:
product -> System.out.println(product)becomes:
System.out::printlnAnd:
product -> product.getName()becomes:
Product::getNameAnd:
(name, price) -> new Product(name, price)can become:
Product::newWhen Should You Use Method References?
Prefer a method reference when:
- an existing method already performs exactly the required operation
- the method is compatible with the required functional interface
- it makes the code easier to read
But don’t force it.
For example, this lambda contains actual business logic:
product ->
product.getPrice() > 10 &&
product.isAvailable()Trying to replace every lambda with a method reference isn’t the goal.
The goal is readability.
Key Takeaways
- Method references provide a concise way to reference existing methods.
- They use the
::operator. - They can reference static methods, instance methods, and constructors.
- There are four common forms:
Class::staticMethod
object::instanceMethod
Class::instanceMethod
Class::new- Like lambdas, method references implement functional interfaces.
- The referenced method must be compatible with the functional interface’s abstract method.
- Use them when they remove an unnecessary lambda layer and make the code clearer.
Remember This
Lambda introduces new behavior
↓
Keep the lambdaLambda only calls an existing method
↓
Consider a method referenceIn short: A method reference says, “I already have the method that performs this behavior — just use that.”
Continue Learning Java Lambdas 🚀
← Previous: Working with Exceptions in Lambda Expressions | Next →: Coming Soon









