Sitemap
Suraj

Suraj

Technical Lead | 14+ yrs specializing in Java, Distributed Systems & System Design.

Method References in Java

4 min readAug 23, 2026

--

Method references are a cleaner and more concise way to use an existing method as a lambda expression.

Press enter or click to view image in full size

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::println

Use 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 :: methodName

For constructors:

ClassName :: new

There 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::staticMethod

Visualize 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::instanceMethod

Another example:

printer::print

means 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::getName

Giving 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::instanceMethod

Think of it as:

obj -> obj.method()

becoming:

ClassName::method

For example:

product -> product.getName()

becomes:

Product::getName

4. 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::new

Conceptually:

ProductFactory.create(...)
│
▼
Product::new
│
▼
new Product(...)

The Four Types — Quick Reference

Press enter or click to view image in full size

A useful memory trick:

STATIC
Class::method
SPECIFIC OBJECT
object::method
ANY OBJECT OF TYPE
Class::method
CONSTRUCTOR
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 Reference

So:

product -> System.out.println(product)

becomes:

System.out::println

And:

product -> product.getName()

becomes:

Product::getName

And:

(name, price) -> new Product(name, price)

can become:

Product::new

When 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 lambda
Lambda only calls an existing method
↓
Consider a method reference

In 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

--

--