skip to content

How do constructor references (ClassName::new) and array-constructor references (int[]::new) work, and which functional interfaces do they satisfy?

level: middleimportance: should knowfreq 55%

answer

  1. ClassName::new = args -> new ClassName(args)
  2. Target interface picks the constructor (overload resolution)
  3. int[]::new = n -> new int[n], an IntFunction<int[]>
  4. Stream.toArray(String[]::new)
  5. Generics inferred from the target type

basics

~20 s

ClassName::new is shorthand for a lambda that calls a constructor — which constructor is picked depends on the target interface's parameters. int[]::new is a special form that creates an array of a given length, like n -> new int[n].

solid answer

~40 s

A constructor reference ClassName::new stands for args -> new ClassName(args). The number and types of arguments are inferred from the target functional interface, so the same reference can satisfy different interfaces: ArrayList::new fits Supplier<List<T>> (() -> new ArrayList<>()), while StringBuilder::new can fit Function<String,StringBuilder> (s -> new StringBuilder(s)). The compiler does normal overload resolution to pick the matching constructor; if none matches or several are equally applicable it fails. The array-constructor reference int[]::new is a special case: it takes a single int length and allocates an array, equivalent to n -> new int[n], satisfying IntFunction<int[]>. Its most common use is Stream.toArray(String[]::new), where the stream supplies the size and the reference allocates a correctly typed array. Generic types are inferred from the target, e.g. HashMap<String,Integer>::new via Supplier.

go deeper

for a junior

Knows ClassName::new makes a new object and int[]::new makes an array of a given size.

for a middle

Can match ::new to Supplier/Function based on constructor arity and use String[]::new with toArray.

for a senior

Can explain target-driven constructor overload resolution and generic inference, and the typed-array benefit of toArray(T[]::new).

for a principal

Can reason about ambiguous-constructor compile failures, design constructors that map cleanly to functional targets, and the performance/typing rationale behind the array-reference idiom.

## Constructor reference: `ClassName::new` A constructor is the special method that builds a new object: `new ArrayList<>()`. A **constructor reference** points at it without writing the `new` wrapper lambda. `ClassName::new` is equivalent to `(args...) -> new ClassName(args...)`. The crucial part: **which constructor and how many arguments** is not written explicitly — it is **inferred from the target functional interface** (the interface the reference is being assigned to). The compiler looks at the abstract method's parameter list and performs normal **overload resolution** over the class's constructors. Examples (same `::new`, different targets): - Target `Supplier<List<String>>` (abstract method `List<String> get()`, no args): `ArrayList::new` ⟶ `() -> new ArrayList<>()` (no-arg constructor). - Target `Function<String,StringBuilder>` (one String arg): `StringBuilder::new` ⟶ `s -> new StringBuilder(s)` (the `StringBuilder(String)` constructor). - Target `BiFunction<String,Integer,SomeBean>`: `SomeBean::new` ⟶ `(s,i) -> new SomeBean(s,i)` (the two-arg constructor). If **no constructor matches** the target's arity/types, or if **more than one** is equally applicable (ambiguous), it is a compile error. **Generics** are inferred from the target type. `HashMap<String,Integer>::new` (or just `HashMap::new` with the target supplying the type args) gives a `Supplier<Map<String,Integer>>`. This is why `Collectors.toCollection(TreeSet::new)` and `Stream.collect(..., ArrayList::new, ...)` work. ## Array-constructor reference: `Type[]::new` Arrays don't have ordinary constructors, so Java provides a **special syntax**: `int[]::new`, `String[]::new`, etc. It is equivalent to `n -> new int[n]` — it takes a single `int` (the **length**) and allocates a new array of that size. It satisfies `IntFunction<int[]>` (for primitives) or `IntFunction<String[]>` (for references). The canonical use is converting a stream to an array: ```java String[] arr = stream.toArray(String[]::new); ``` `Stream.toArray(IntFunction<A[]>)` calls the reference with the element count it computed, and the reference allocates a `String[]` of exactly that size. Using `String[]::new` (rather than `Object[]::new`) gives you a correctly typed array instead of an `Object[]`. You cannot write `int[]::new` to mean a multi-dimensional or pre-filled array; the length is the only argument. ## How resolution ties together For both forms the rule is the same as for other references: the **target functional interface drives everything**. The reference has no fixed arity of its own — the abstract method's parameters determine which constructor (or the array length) is used. This is what lets one short token (`::new`) adapt to `Supplier`, `Function`, `BiFunction`, or `IntFunction` depending on context. ## Common uses - Factories: `Supplier<StringBuilder> f = StringBuilder::new;` - Stream collectors: `.collect(Collectors.toCollection(LinkedList::new))`. - Mapping to objects: `names.stream().map(User::new)` where `User` has a `User(String)` constructor. - Array materialization: `.toArray(Integer[]::new)`.

  • Why prefer stream.toArray(String[]::new) over stream.toArray()?
    The no-arg toArray() returns an Object[]; passing String[]::new produces a correctly typed String[] without an unsafe cast, because the reference allocates an array of the right component type.
  • Can ArrayList::new satisfy both a Supplier and a Function?
    Yes, depending on the target: a Supplier target picks the no-arg constructor (() -> new ArrayList<>()), while a Function<Integer,List> target could pick the ArrayList(int capacity) constructor. The same reference adapts via overload resolution.

saying these in an interview costs you the question

  • Thinking ::new always means the no-arg constructor — the arity comes from the target interface.
  • Believing int[]::new can pre-fill or size a 2D array — it only takes one length argument.
  • Assuming the array reference returns Object[] — it returns the correctly typed component array.

context