skip to content

How does a constructor reference (::ClassName) work, and how would you use it as a factory passed to map?

level: middleimportance: must knowfreq 50%

answer

  1. ::ClassName == (params) -> Instance
  2. ids.map(::User) replaces { id -> User(id) }
  3. Type is KFunction (e.g. KFunction1)
  4. Target type disambiguates overloaded constructors
  5. No constructor ref for abstract/interface

basics

~20 s

::ClassName is a value that creates new objects of that class. Its parameters match the constructor, so you can pass it where a function is expected, like ids.map(::User) to turn each id into a User.

solid answer

~30 s

A **constructor reference** `::ClassName` is a callable reference whose call signature matches the class's constructor, returning a new instance. So if `User(id: Int)` exists, `::User` is effectively `(Int) -> User` and can be passed to higher-order functions: `ids.map(::User)`. Its reflection type is **`KFunction`** (e.g. `KFunction1<Int, User>`). For multiple constructors, the expected target type disambiguates which one is selected. It composes nicely: `lines.map(::trim).map(::User)`. Because it is just a function value, you can also store it (`val make: (Int) -> User = ::User`) and inject it as a factory dependency, avoiding `{ id -> User(id) }` boilerplate.

code

kotlin · 3 lines
kotlin
data class Event(val name: String)
val names = listOf("login", "logout")
val events = names.map(::Event) // [Event(login), Event(logout)]

go deeper

for a junior

Knows ids.map(::User) makes Users from ids.

for a middle

Explains ::Class as a function value, uses it as a factory, knows it replaces wrapping lambdas.

for a senior

Handles overload disambiguation via expected type and KFunction typing; uses it for DI factories.

for a principal

Reasons about inference limits, abstract/interface restrictions, and API design where constructor refs improve testability.

## Constructor references A **constructor reference** is written `::ClassName` and is a **callable reference** (via the `::` operator) that, when invoked, **constructs a new instance**. Its parameter list matches a constructor of the class, and its return type is the class. ```kotlin class User(val id: Int) val ids = listOf(1, 2, 3) val users: List<User> = ids.map(::User) // one User per id ``` Here `::User` acts as a value of type `(Int) -> User`, so it slots straight into `map` instead of a lambda `{ id -> User(id) }`. ## Reflection type The runtime type is a **`KFunction`** subtype — for a one-arg constructor, `KFunction1<Int, User>`. It exposes `.name` (`"<init>"`), `.parameters`, and `.call(...)`. You usually just use it as a plain function type. ## Disambiguation with overloads A class can have several constructors. The **expected (target) type** at the use site picks the right one: ```kotlin class Point(val x: Int, val y: Int) { constructor(v: Int) : this(v, v) } val f1: (Int) -> Point = ::Point // the (Int) constructor val f2: (Int, Int) -> Point = ::Point // the (Int, Int) constructor ``` If the context is ambiguous, you must supply an explicit function type to resolve it. ## As a factory dependency Because it is a first-class function, a constructor reference is a clean **factory**: ```kotlin class Service(private val makeUser: (Int) -> User = ::User) { fun load(id: Int) = makeUser(id) } ``` This is more concise than a wrapping lambda and reads as "the User constructor." ## Limits / notes - You cannot reference a constructor of an **abstract class** or **interface** (nothing to construct). - Generic classes infer type arguments from context; you may need to help inference with an explicit type. - Works with **data classes**, regular classes, and nested/inner classes (for inner, you need a bound reference to an outer instance).

  • A class has two constructors with the same arity differing only by parameter type. How does ::ClassName pick one?
    By the expected function type at the call site (parameter and return types). If that is insufficient to disambiguate, the code won't compile and you provide an explicit typed target.
  • Can you take a constructor reference to an interface?
    No. Interfaces and abstract classes cannot be instantiated, so there is no constructor to reference.

Like passing the blueprint of a product to an assembly line so it stamps out copies, instead of writing build instructions each time.

saying these in an interview costs you the question

  • Saying ::User calls the constructor immediately instead of producing a function value
  • Believing you need a lambda { id -> User(id) } and ::User cannot be used
  • Not knowing the reflection type is KFunction
  • Thinking abstract classes/interfaces can have constructor references

context