How does the compiler resolve a method reference against a target functional interface, including overload disambiguation and when it fails?
answer
- A method reference is a poly expression — needs a target type
- Target = functional interface's single abstract method signature
- ClassName::m tries static AND unbound; both applicable = ambiguous
- Boxing/widening allowed; return must be assignable (or void)
- Ambiguity fix = expand to an explicit lambda
basics
~20 sThe compiler looks at the functional interface the reference must satisfy, then finds a method (or constructor) whose signature is compatible — possibly treating the first parameter as the receiver. If several methods fit equally, or none do, it's a compile error.
solid answer
~50 sA method reference has no meaning in isolation; it only type-checks against a target type — a functional interface with one abstract method. The compiler takes that abstract method's parameter and return types and searches the named class for a matching method or constructor, applying the same applicability and overload-resolution rules as a normal call. For ClassName::method it considers two readings: a static method taking all the abstract method's parameters, and an instance method where the first parameter is the receiver and the rest are arguments. If both readings produce an applicable candidate, the reference is ambiguous and rejected. Overloaded target methods are filtered by arity and assignability, with autoboxing and widening allowed. Generic type arguments are inferred from the target. Resolution fails when no candidate is applicable, when the result type isn't assignable to the abstract method's return type, or when two candidates are equally applicable. In ambiguous cases you fall back to an explicit lambda, which forces one interpretation.
go deeper
Knows a method reference must match the interface it's assigned to and that mismatches cause compile errors.
Can check arity/return-type compatibility and recognize that the first parameter may be the receiver.
Can explain static-vs-unbound ambiguity, applicability with boxing/widening, generic inference from the target, and the lambda escape hatch.
Can predict resolution against overloaded/generic APIs, design functional interfaces and method names that avoid ambiguity, and reason about how poly-expression typing interacts with overload resolution at call sites.
## The core idea: references are poly expressions A **method reference** like `String::valueOf` has **no standalone type**. In Java terms it is a **poly expression**: its meaning depends on its **target type** — the type the surrounding context requires. That target type must be a **functional interface**: an interface with exactly one **abstract method** (the SAM — Single Abstract Method). The reference becomes an implementation of that one method. So resolution is always: *given this functional interface's abstract method signature, is there a method/constructor I can bind to that satisfies it?* ## Step 1: extract the target signature Take the abstract method's parameter types `(P1, P2, ...)` and return type `R`. Example: target `Comparator<String>` has `int compare(String, String)`, so the signature is `(String, String) -> int`. ## Step 2: find compatible candidates in the named type For `ClassName::name`, the compiler considers several **kinds** of bindings: 1. **Static** method `name` taking `(P1, P2, ...)` and returning something assignable to `R`. 2. **Unbound instance** method `name` where `P1` is the receiver type and the method takes `(P2, ...)`. 3. (For an object expression `obj::name`) a **bound instance** method taking `(P1, ...)`. 4. (For `::new`) a **constructor** taking `(P1, P2, ...)`. Within each kind, normal **overload resolution** applies: candidates are filtered by **applicability** (arity match; each argument assignable, allowing **autoboxing/unboxing** and **widening primitive/reference conversions**), then the **most specific** applicable one is chosen. ## Step 3: disambiguation rules and failures The interesting failure modes: - **Static vs. unbound clash.** If `ClassName` has *both* a static method and an instance method named `name` that are *each* applicable to the target, the reference is **ambiguous** — the compiler cannot decide reading (1) vs. reading (2) — and reports an error. Example: a type with `static foo(X)` and instance `foo()` can make `T::foo` ambiguous against `Function<T,?>` because both the static (`foo(T)`) and the unbound instance (`t.foo()`) readings are applicable. - **No applicable candidate.** If no method/constructor matches the arity or types (even after boxing/widening), it fails: e.g. `Integer::parseInt` against a `Supplier<Integer>` (wrong arity — `parseInt` needs a `String`). - **Return type mismatch.** The chosen method's return must be assignable to `R` (or `R` is `void`, which accepts any return and discards it). `String::isEmpty` (returns `boolean`) cannot satisfy a `Function<String,String>`. - **Equally-specific overloads.** If two overloads of the target method remain equally applicable, the reference may be ambiguous; this often surfaces with overloaded library methods where you must cast or use a lambda. ## Step 4: generics and inference Type arguments of the target (e.g. `Function<String,Integer>`) feed **type inference** so the reference resolves the right generic method/constructor. `Collections::sort`, `HashMap::new`, etc. all rely on inferring type parameters from the target. ## Why a lambda is the escape hatch Because a lambda **writes the parameters explicitly**, it forces exactly one interpretation and disambiguates which overload you mean. So the standard fix for an ambiguous or non-resolving method reference is to expand it to a lambda: instead of `T::foo`, write `t -> t.foo()` (or `t -> T.foo(t)`), making the receiver/argument mapping unambiguous. ## Mental checklist when a reference won't compile 1. Is the target actually a functional interface (one abstract method)? 2. Does the arity match, counting the receiver as the first parameter for the unbound form? 3. Is the return type assignable (or is the target `void`)? 4. Are there competing static/instance or overloaded candidates? If so, switch to a lambda.
- Why does expanding to a lambda often fix an ambiguous method reference?A lambda names its parameters and the call explicitly, so it commits to one receiver/argument mapping and one overload, removing the ambiguity the bare reference left for the compiler to resolve.
- Can a method reference target an interface with two abstract methods?No. The target must be a functional interface — exactly one abstract method (default/static/Object methods don't count). Otherwise there is no single signature to bind to.
- Does the compiler allow autoboxing during reference resolution?Yes. Applicability uses the same conversions as a method call, including autoboxing/unboxing and widening, so e.g. an int-returning method can satisfy a target whose abstract method returns Integer.
saying these in an interview costs you the question
- Saying a method reference has a fixed type on its own — it is a poly expression that depends on the target.
- Forgetting the unbound reading exists, so missing why ClassName::m can be ambiguous with a static m.
- Claiming any interface can be a target — it must have exactly one abstract method.