Question

Kotlin Constructor References: Using ::ClassName for Factories

kotlinfunction-referenceconstructor-reference

Question

Java supports constructor references such as ClassName::new. Kotlin has callable references for methods, but does it also support constructor references? How can constructor references be used when building factories?

Short Answer

Kotlin supports constructor references. Instead of Java's ClassName::new, Kotlin uses ::ClassName. A constructor reference is a function value that can be stored, passed to another function, or invoked later—making it useful for factories, collection transformations, and dependency wiring.

Concept

A constructor reference points to a class constructor without creating an object immediately.

In Kotlin, its syntax is:

::ClassName

For example, if User has a constructor accepting a String, then ::User behaves like a function of type (String) -> User.

class User(val name: String)

val createUser: (String) -> User = ::User
val user = createUser("Ada")

Constructor references matter because Kotlin functions are values. You can pass ::User into a factory method, save it in a variable, or use it with functions such as map. This avoids writing a small lambda such as { name -> User(name) } when a direct constructor call is all that is needed.

The referenced constructor must match the parameter and return types expected at the place where you use it.

Mental Model

Think of a constructor reference as a recipe card rather than a finished meal.

  • User("Ada") follows the recipe now and creates a User immediately.
  • ::User hands someone the recipe card. They can use it later whenever they receive the required ingredients.

If the User constructor needs a name, ::User is a recipe that accepts one String and produces one User. In function-type notation, that is (String) -> User.

Take Quiz

Syntax and Examples

The basic syntax is ::ClassName.

class Product(val id: Int, val name: String)

val createProduct: (Int, String) -> Product = ::Product

val keyboard = createProduct(101, "Keyboard")
println(keyboard.name) // Keyboard

::Product refers to the constructor:

Product(id: Int, name: String)

Therefore, Kotlin can infer that the reference has type (Int, String) -> Product.

A constructor reference is often equivalent to a lambda:

val byReference: (String) -> User = ::User
val byLambda: (String) -> User = { name -> User(name) }

Both work, but ::User is shorter and directly communicates that the constructor is being called.

Default constructors

A class with no constructor parameters can be referenced as () -> ClassName:

class Session

val createSession: () -> Session = ::Session
val session = createSession()

Using a constructor reference with map

data class Tag(val text: String)

val names = listOf("kotlin", "java", "python")
val tags = names.map(::Tag)

println(tags) // [Tag(text=kotlin), Tag(text=java), Tag(text=python)]

Step by Step Execution

Consider this factory helper:

class Account(val email: String)

fun <T> create(value: String, factory: (String) -> T): T {
    return factory(value)
}

val account = create("ada@example.com", ::Account)

Execution proceeds as follows:

  1. Account declares a constructor that needs one String.
  2. ::Account creates a reference to that constructor. Its effective type is (String) -> Account.
  3. create receives the text "ada@example.com" and the constructor reference as factory.
  4. Inside create, factory(value) invokes the referenced Account constructor.
  5. Kotlin evaluates this as if it had run Account("ada@example.com").
  6. The new Account instance is returned and assigned to account.

The constructor is not called when ::Account is written. It is called only when the function value is invoked with factory(value).

Real World Use Cases

Constructor references are useful whenever data must be turned into objects consistently.

  • Mapping API data: Convert a list of strings or parsed values into domain objects.

    data class Email(val value: String)
    val emails = rawEmails.map(::Email)
    
  • Factory functions: Let a reusable factory receive the type of object it should create.

    fun <T> makeMany(values: List<String>, create: (String) -> T): List<T> =
        values.map(create)
    
  • UI state creation: Convert event data into immutable state or action objects.

  • Parser pipelines: Pass a constructor to convert validated fields into records.

  • Test data: Supply constructors to generic fixture builders when multiple classes share the same input shape.

Take Quiz

Real Codebase Usage

In production code, constructor references commonly appear at transformation boundaries: API response to domain model, database row to application object, or input text to a small value object.

Mapping with validation first

A constructor reference does not replace validation. Validate input, then construct the object.

@JvmInline
value class UserId(val value: String)

fun parseUserId(raw: String): UserId? {
    if (raw.isBlank()) return null
    return ::UserId(raw.trim())
}

In this small example, calling UserId(raw.trim()) is clearer than using a reference. Constructor references are most helpful when the constructor is passed to another function.

A reusable mapping helper

fun <I, O> convertAll(items: Iterable<I>, converter: (I) -> O): List<O> {
    return items.map(converter)
}

data class Category(val name: String)

val categories = convertAll(listOf("Books", "Games"), ::Category)

Overloaded constructors

If a class has more than one constructor, Kotlin may need an expected function type to select the right one.

class Message {
    constructor(text: String)
    constructor(code: Int)
}

val fromText: (String) -> Message = ::Message
val fromCode: (Int) -> Message = ::Message

The declared types tell Kotlin which constructor reference is intended.

Common Mistakes

Using Java syntax in Kotlin

This is Java syntax, not Kotlin:

// Does not compile in Kotlin
val factory = User::new

Use Kotlin's syntax instead:

val factory: (String) -> User = ::User

Forgetting to invoke the reference

::User is a function value, not a User object.

class User(val name: String)

val factory: (String) -> User = ::User
// val user: User = ::User // Does not compile

val user = factory("Ada")

Passing arguments to the reference itself

This is invalid because ::User only obtains the reference:

// val user = ::User("Ada") // Does not compile

Store or pass the reference, then invoke it:

val factory: (String) -> User = ::User
val user = factory("Ada")

Ignoring overload ambiguity

When a class has multiple constructors, ::ClassName may not provide enough information on its own.

class NumberBox {
    constructor(value: Int)
    constructor(value: String)
}

// val factory = ::NumberBox // May be ambiguous
val factory: (Int) -> NumberBox = ::NumberBox

Provide an expected type or use a lambda if you need extra conversion logic.

Using a reference when a lambda adds necessary work

A constructor reference cannot trim, validate, or rearrange arguments by itself:

data class User(val name: String)

val createUser: (String) -> User = { rawName ->
    User(rawName.trim())
}

Use a lambda when object creation needs logic beyond directly calling the constructor.

Comparisons

TaskKotlin syntaxMeaning
Call a constructor nowUser("Ada")Creates an object immediately.
Refer to a constructor::UserProduces a callable function value.
Call an instance method nowuser.displayName()Invokes the method immediately.
Refer to an instance methoduser::displayNameProduces a function bound to user.
Refer to a static-like function::parse or Parser::parseProduces a function reference.
Java constructor referenceUser::newJava equivalent of Kotlin ::User.

A lambda and constructor reference can often do the same job:

val a: (String) -> User = ::User
val b: (String) -> User = { name -> User(name) }

Prefer the constructor reference when it is a direct constructor call. Prefer a lambda when you must transform input, choose between constructors, or add validation.

Take Quiz

Cheat Sheet

// Class with one constructor parameter
class User(val name: String)
val createUser: (String) -> User = ::User
val user = createUser("Ada")

// No-argument constructor
class Cache
val createCache: () -> Cache = ::Cache

// Two constructor parameters
data class Point(val x: Int, val y: Int)
val createPoint: (Int, Int) -> Point = ::Point

// Collection mapping
val users = names.map(::User)

// Resolve overloaded constructors with an expected type
val fromName: (String) -> User = ::User

Rules to remember:

  • Kotlin uses ::ClassName, not ClassName::new.
  • The reference type mirrors constructor parameters and returns the class type.
  • Writing ::ClassName does not create an instance.
  • Invoke the stored reference with parentheses, such as factory(arguments).
  • Add an expected function type when overloaded constructors are ambiguous.
  • Use a lambda instead when construction requires additional logic.

FAQ

Does Kotlin have constructor references?

Yes. Use ::ClassName, such as ::User, to refer to a constructor.

What is the Kotlin equivalent of Java ClassName::new?

The Kotlin equivalent is ::ClassName. For example, Java User::new is Kotlin ::User.

What type does a constructor reference have in Kotlin?

Its function type matches the constructor. If User accepts a String, ::User can have type (String) -> User.

Can I use a Kotlin constructor reference as a factory?

Yes. Pass it to a function that accepts a matching function type, such as (String) -> T.

Can constructor references use constructors with multiple arguments?

Yes. A constructor such as Point(Int, Int) can be referenced as ::Point with type (Int, Int) -> Point.

What happens when a class has overloaded constructors?

Kotlin needs context to choose one. Assign ::ClassName to a specific function type, or use a lambda to make the choice explicit.

Can a constructor reference apply default argument values automatically?

A function type calls with exactly the parameters in that function type. If you want to omit arguments or customize defaults in a factory, use a lambda that calls the constructor with the arguments you want.

Take Quiz

Mini Project

Description

Build a small import utility that turns raw product names into Product objects. It demonstrates passing a Kotlin constructor reference to a generic factory function and using it with collection mapping.

Goal

Create Product instances from a list of names by passing ::Product as a factory.

Requirements

  1. Create a Product data class with a single name property.
  2. Create a generic function that accepts a list of strings and a (String) -> T factory.
  3. Use the factory to convert every input string into an object.
  4. Pass ::Product to the generic function.
  5. Print the resulting product list.
Take Quiz

Keep learning

Related questions

View more