Question
Kotlin Constructor References: Using ::ClassName for Factories
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 aUserimmediately.::Userhands 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.
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:
Accountdeclares a constructor that needs oneString.::Accountcreates a reference to that constructor. Its effective type is(String) -> Account.createreceives the text"ada@example.com"and the constructor reference asfactory.- Inside
create,factory(value)invokes the referencedAccountconstructor. - Kotlin evaluates this as if it had run
Account("ada@example.com"). - The new
Accountinstance is returned and assigned toaccount.
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.
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
| Task | Kotlin syntax | Meaning |
|---|---|---|
| Call a constructor now | User("Ada") | Creates an object immediately. |
| Refer to a constructor | ::User | Produces a callable function value. |
| Call an instance method now | user.displayName() | Invokes the method immediately. |
| Refer to an instance method | user::displayName | Produces a function bound to user. |
| Refer to a static-like function | ::parse or Parser::parse | Produces a function reference. |
| Java constructor reference | User::new | Java 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.
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, notClassName::new. - The reference type mirrors constructor parameters and returns the class type.
- Writing
::ClassNamedoes 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.
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
- Create a
Productdata class with a singlenameproperty. - Create a generic function that accepts a list of strings and a
(String) -> Tfactory. - Use the factory to convert every input string into an object.
- Pass
::Productto the generic function. - Print the resulting product list.
Keep learning
Related questions
Accessing Kotlin Extension Functions from Java
Learn how Kotlin extension functions are compiled and how to call them correctly from Java with clear examples and common pitfalls.
Add a Maven Repository URL in Gradle Kotlin DSL (build.gradle.kts)
Learn how to add Maven Central and a custom Maven repository URL in Gradle Kotlin DSL using build.gradle.kts syntax.
Allow HTTP and HTTPS in Android 9 Pie with Network Security Configuration
Learn how Android 9 Pie handles cleartext HTTP traffic and how to allow HTTP and HTTPS safely using network security config.