Method References — The Four Forms and Their Lambda Equivalents
Fast recall
Quick revision
Core answer
ClassName::staticMethod, e.g., Integer::parseInt, equivalent to s -> Integer.parseInt(s); (2) Bound instance method reference — instance::method, e.g., System.out::println, equivalent to x -> System.out.println(x), where the receiver is captured at lambda creation time; (3) Unbound instance method reference — ClassName::instanceMethod, e.g., String::toUpperCase, equivalent to s -> s.toUpperCase(), where the first argument becomes the receiver; (4) Constructor reference — ClassName::new, e.g., ArrayList::new (a Supplier), or User::new (a Function for a constructor taking a String). The compiler infers which form based on context and method overloads. Use method references when they read more clearly than the lambda; stick with a lambda when you need parameter rearrangement, multiple operations, or when the method-reference form is ambiguous (multiple matching overloads). Method references are usually slightly more efficient (one less indirection) and signal intent more clearly than a lambda calling one method.Whether the candidate can identify all four forms, write the lambda equivalent for each, and choose between method reference and lambda based on readability
Confusing bound (instance::method) with unbound (Class::method) instance references. Forgetting that `Class::instanceMethod` makes the first argument the receiver. Not knowing constructor references match Supplier or Function based on parameters
Show how method references resolve overloaded methods (compiler picks based on functional interface), explain the bound vs unbound distinction with a concrete example, and discuss when method references can be slightly faster than lambdas (cached call site, no synthetic lambda body)
Interview-ready guide
Interview answer
Method references are a compact lambda syntax for the case where the lambda just calls an existing method. The :: operator separates the type/instance from the method name. There are four forms:
1. Static method reference — `ClassName::staticMethod`
- Refers to a static method of a class
- Example:
Integer::parseInt, equivalent tos -> Integer.parseInt(s) - Other examples:
Math::max,Objects::requireNonNull,String::valueOf - Lambda parameters become method arguments, in order
2. Bound instance method reference — `instance::method`
- Receiver is captured at the moment the reference is created
- Example:
System.out::println, equivalent tos -> System.out.println(s) - The
System.outPrintStream is captured; lambda parameters are method arguments - Other examples:
userRepo::save,cache::get,logger::error - Be careful: if the receiver is null at creation time, you get NPE immediately
3. Unbound instance method reference — `ClassName::instanceMethod`
- The first lambda parameter becomes the receiver
- Example:
String::toUpperCase, equivalent tos -> s.toUpperCase() - BiFunction example:
String::equalsIgnoreCaseis(a, b) -> a.equalsIgnoreCase(b)— first arg is receiver, second is method argument - Common in stream maps:
User::getName,Order::getTotal
4. Constructor reference — `ClassName::new`
- Refers to a constructor; the compiler picks the overload by parameter count
ArrayList::newis aSupplierfor the no-arg constructorUser::newcould matchFunctionforUser(String)orBiFunctionforUser(String, String)- Array constructor:
String[]::newisIntFunctionforstream.toArray(...)
Bound vs unbound — the trickiest distinction
String s = "hello";
Supplier<Integer> bound = s::length; // s captured, no args
Function<String, Integer> unbound = String::length; // arg becomes receiver- Bound: looks like
instance::method— receiver fixed - Unbound: looks like
Class::methodandmethodis an instance method — first arg is receiver - Static: looks like
Class::methodandmethodis a static method
The compiler disambiguates based on whether the method is static and what target functional interface is expected.
Lambda vs method reference — when to use which
Use method reference when:
- The lambda just calls one method
- The method name is self-documenting
- Common cases:
User::getName,Objects::nonNull,String::isEmpty,System.out::println
Use lambda when:
- You need to rearrange parameters
- You need multiple operations
- You need to add a constant
- The method-reference form is ambiguous
Performance
- Method references are usually as fast or marginally faster than lambdas
- Non-capturing static and unbound references are cached — zero allocation per call
- Bound references capture the receiver — one allocation per capture (small cost)
- Constructor references in hot paths: watch allocation; usually fine
Pitfalls
- Bound vs unbound confusion — the most common interview trap
- Ambiguous overloads — if multiple methods match the target signature, the compiler errors out; sometimes you need a lambda for explicit disambiguation
- `null` receiver in bound reference — NPE at creation time; the receiver is evaluated eagerly
- Mutable captured receiver — bound reference holds the instance reference; if it mutates, behavior changes
Recommendation: Use method references as the default when the lambda just calls one method — they read better, document intent, and are sometimes faster. Stick with lambdas when you need parameter rearrangement, multiple operations, or to add constants. The trickiest case is bound vs unbound — remember the rule: instance::method is bound (receiver fixed), Class::method on an instance method is unbound (first arg becomes receiver). For constructor references, the compiler picks the overload by parameter count, so User::new can be a Supplier, a Function, or a BiFunction depending on context. Trust your IDE's hints — IntelliJ and Eclipse will suggest converting x -> foo(x) to this::foo or MyClass::foo automatically; usually accept those unless the lambda was doing something non-trivial.
Method References — The Four Forms and Their Lambda Equivalents
Direct Answer
Method references (Java 8+) are a compact lambda syntax for the case where the lambda just calls an existing method. The four forms are: (1) Static method reference — ClassName::staticMethod, e.g., Integer::parseInt, equivalent to s -> Integer.parseInt(s); (2) Bound instance method reference — instance::method, e.g., System.out::println, equivalent to x -> System.out.println(x), where the receiver is captured at lambda creation time; (3) Unbound instance method reference — ClassName::instanceMethod, e.g., String::toUpperCase, equivalent to s -> s.toUpperCase(), where the first argument becomes the receiver; (4) Constructor reference — ClassName::new, e.g., ArrayList::new (a Supplier<ArrayList>), or User::new (a Function<String, User> for a constructor taking a String). The compiler infers which form based on context and method overloads. Use method references when they read more clearly than the lambda; stick with a lambda when you need parameter rearrangement, multiple operations, or when the method-reference form is ambiguous (multiple matching overloads). Method references are usually slightly more efficient (one less indirection) and signal intent more clearly than a lambda calling one method.
Common Mistakes & Pitfalls
Confusing bound (instance::method) with unbound (Class::method) instance references. Forgetting that Class::instanceMethod makes the first argument the receiver. Not knowing constructor references match Supplier or Function based on parameters
Check your understanding
Answer these without looking back at the article.
- What's the difference between `s::length` and `String::length`?
- Why would `User::new` work both as a `Supplier` and as a `Function`?
- How does the compiler resolve `System.out::println` when there are multiple `println` overloads?
- What happens if you create a bound method reference with a `null` instance?
- When can a method reference be slightly faster than the equivalent lambda?
Related interview prep categories
1Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.