skip to content

What are the four kinds of method references in Java, and how is each written?

level: juniorimportance: must knowfreq 72%

answer

  1. Static / bound / unbound / constructor
  2. Bound = receiver fixed; unbound = receiver is first arg
  3. System.out::println vs String::toUpperCase
  4. ClassName::new and int[]::new
  5. Always shorthand for a one-call lambda

basics

~10 s

A method reference is shorthand for a lambda that just calls one method. The four kinds are: static (Class::staticMethod), bound instance (object::method), unbound instance (Class::instanceMethod), and constructor (Class::new).

solid answer

~40 s

A method reference (the :: operator) is concise syntax for a lambda whose body just calls an existing method. There are four forms. Static: ClassName::staticMethod, e.g. Integer::parseInt. Bound instance: instance::method, where the receiver object is fixed/captured at the point of the reference, e.g. System.out::println. Unbound instance: ClassName::instanceMethod, where the receiver is supplied as the first argument at call time, e.g. String::toUpperCase. Constructor: ClassName::new, which creates a new instance, e.g. ArrayList::new. There is also the array-constructor variant int[]::new. Each compiles to the same kind of functional-interface instance a lambda would, and is chosen for readability when no extra arguments or logic are needed.

go deeper

for a junior

Can name the four forms and give one example of each, and knows :: is shorthand for a one-call lambda.

for a middle

Can explain the bound-vs-unbound distinction and which functional interface each form satisfies.

for a senior

Can articulate how the receiver-as-first-parameter rule maps to interfaces like Comparator and Function, and the array-constructor variant.

for a principal

Can reason about overload/target-type resolution, ambiguity cases, capture semantics of bound references, and guide when a reference helps vs. hurts readability in an API.

## What problem method references solve A **lambda expression** in Java is an inline anonymous function, e.g. `s -> s.length()`. A **functional interface** is an interface with exactly one abstract method (e.g. `Function<T,R>` with `R apply(T)`, `Supplier<T>` with `T get()`, `Consumer<T>` with `void accept(T)`). A lambda or method reference is an *implementation* of that single abstract method. Very often a lambda does nothing but forward its arguments to an already-existing method: `s -> Integer.parseInt(s)` or `s -> s.toUpperCase()`. A **method reference**, written with the `::` operator, is a shorthand for exactly that pattern: "don't write a wrapper lambda, just point at the method I want to call." ## The four kinds **1. Reference to a static method** — `ClassName::staticMethod`. Equivalent lambda: `args -> ClassName.staticMethod(args)`. Example: `Integer::parseInt` is the same as `s -> Integer.parseInt(s)`. It satisfies `Function<String,Integer>`. **2. Reference to an instance method of a *particular* object (bound)** — `instance::method`. The **receiver** (the object the method runs on) is **fixed/captured** when you write the reference. Equivalent lambda: `args -> instance.method(args)`. Example: `System.out::println` captures the specific `System.out` object; it is the same as `x -> System.out.println(x)`. Satisfies `Consumer<T>`. Because the receiver is captured, the reference holds onto that object. **3. Reference to an instance method of an *arbitrary* object of a type (unbound)** — `ClassName::instanceMethod`. Here the receiver is **not** fixed; it is supplied as the **first parameter** at call time. Equivalent lambda: `(receiver, otherArgs) -> receiver.method(otherArgs)`. Example: `String::toUpperCase` is the same as `s -> s.toUpperCase()`; it satisfies `Function<String,String>` where the single argument *is* the receiver. `String::compareTo` satisfies `Comparator<String>` = `(a,b) -> a.compareTo(b)`: the first param is the receiver, the second is the method argument. **4. Reference to a constructor** — `ClassName::new`. Equivalent lambda: `args -> new ClassName(args)`. Example: `ArrayList::new` satisfies `Supplier<List<String>>` (`() -> new ArrayList<>()`), and `StringBuilder::new` can satisfy `Function<String,StringBuilder>` (`s -> new StringBuilder(s)`) — the constructor chosen by overload resolution depends on the target's parameter list. **Array-constructor variant** — `int[]::new` (or `String[]::new`) is a special constructor reference that creates an array of a given length: equivalent to `n -> new int[n]`, satisfying `IntFunction<int[]>`. It is heavily used with `Stream.toArray(String[]::new)`. ## Why the distinction matters The key confusion is between **bound** (form 2) and **unbound** (form 3) — both look like `something::method`. The rule: if the part before `::` is an **object/expression**, the receiver is bound (captured). If it is a **type name** and the abstract method's first parameter matches the type, the receiver is unbound (passed in). The compiler decides which reading is legal against the target functional interface. All four kinds compile to an instance of the target functional interface — semantically identical to the equivalent lambda; the choice is purely about clarity.

  • Is a method reference ever more powerful than a lambda?
    No. It is strictly a shorthand for a lambda that makes a single call. Anything a method reference does, an equivalent lambda can express; the reverse is not true (lambdas can add logic, reorder args, etc.).
  • What functional interface does Math::max satisfy?
    Any two-arg interface matching its signature, e.g. IntBinaryOperator ((a,b)->Math.max(a,b)) or BinaryOperator<Integer> via boxing.

Think of a method reference as speed-dial. Static and constructor are dialing a fixed number. A bound reference is a contact saved to one specific person. An unbound reference is a template where you fill in 'who to call' (the receiver) at the moment you dial.

saying these in an interview costs you the question

  • Saying a method reference 'is a method' — it is an expression that produces a functional-interface instance.
  • Claiming method references are faster or fundamentally different from lambdas — they are equivalent, chosen for readability.
  • Confusing bound and unbound: thinking ClassName::method always means a static call.

context