If you’ve been working with Kotlin for a while, you probably know how concise and expressive the language is. One of the features that makes Kotlin so enjoyable is its support for constructor references. This feature allows you to treat a constructor like a function and use it wherever a function is expected.
In this post, we’ll break down how to create instances with constructor references in Kotlin, step by step, using examples that are easy to follow. By the end, you’ll know exactly how and when to use this feature in real-world applications.
What are Constructor References in Kotlin?
In Kotlin, functions and constructors can be passed around just like any other value. A constructor reference is simply a shorthand way of pointing to a class’s constructor.
You use the :: operator before the class name to create a reference. For example:
class User(val name: String, val age: Int)
// Constructor reference
val userConstructor = ::UserHere, ::User is a reference to the User class constructor. Instead of calling User("amol", 25) directly, we can use the userConstructor variable as if it were a function.
Why Use Constructor References?
You might be wondering, why not just call the constructor directly?
Constructor references shine in situations where you need to pass a constructor as an argument. This is common when working with higher-order functions, factory patterns, or functional-style APIs like map.
It keeps your code clean, avoids repetition, and makes your intent very clear.
Creating Instances with Constructor References
Let’s walk through a few practical examples of how to create instances with constructor references in Kotlin.
class Person(val name: String, val age: Int)
fun main() {
// Create a reference to the constructor
val personConstructor = ::Person
// Use the reference to create instances
val person1 = personConstructor("amol", 30)
val person2 = personConstructor("ashvini", 25)
println(person1.name) // amol
println(person2.age) // 25
}::Personis a function reference to the constructor ofPerson.- You can then call it like any function:
personConstructor("amol", 30).
Using with Higher-Order Functions
Suppose you have a list of names and ages, and you want to turn them into Person objects. Instead of writing a lambda, you can pass the constructor reference directly.
data class Person(val name: String, val age: Int)
fun main() {
val peopleData = listOf(
"amol" to 30,
"ashvini" to 25,
"swaraj" to 28
)
// Map each pair into a Person instance using constructor reference
val people = peopleData.map { (name, age) -> Person(name, age) }
println(people)
}Now, let’s make it even cleaner using a constructor reference:
val people = peopleData.map { Person(it.first, it.second) }While Kotlin doesn’t allow passing ::Person directly here (because the data is a Pair), constructor references can still simplify code in similar contexts.
With Function Types
Constructor references can also be stored in variables with a function type.
class Car(val brand: String)
fun main() {
// Function type (String) -> Car
val carFactory: (String) -> Car = ::Car
val car = carFactory("Tesla")
println(car.brand) // Tesla
}::Carmatches the function type(String) -> Car.- Whenever you call
carFactory("Tesla"), it creates a newCarinstance.
Primary vs Secondary Constructors
Kotlin classes can have both primary and secondary constructors. Constructor references can point to either.
class Student(val name: String) {
constructor(name: String, age: Int) : this(name) {
println("Secondary constructor called with age $age")
}
}
fun main() {
val primaryRef: (String) -> Student = ::Student
val secondaryRef: (String, Int) -> Student = ::Student
val student1 = primaryRef("amol")
val student2 = secondaryRef("ashvini", 20)
}Here:
primaryRefpoints to the primary constructor.secondaryRefpoints to the secondary constructor.
Real-World Use Case: Dependency Injection
In frameworks like Koin or Dagger, you often need to tell the system how to create objects. Constructor references make this simple:
class Repository(val db: Database)
class Database
fun main() {
val dbFactory: () -> Database = ::Database
val repoFactory: (Database) -> Repository = ::Repository
val db = dbFactory()
val repo = repoFactory(db)
println(repo.db) // Instance of Database
}This pattern is common when wiring dependencies because you can pass constructor references instead of custom lambdas.
Key Takeaways
- Constructor references allow you to treat constructors like functions in Kotlin.
- Use
::ClassNameto get a reference. - They’re especially useful with higher-order functions, dependency injection, and factory patterns.
- You can reference both primary and secondary constructors.
- They make your code cleaner, shorter, and easier to maintain.
Conclusion
Knowing how to create instances with constructor references in Kotlin is a small but powerful tool in your Kotlin toolkit. It makes functional programming patterns more natural, simplifies object creation in higher-order functions, and improves readability.
If you want your Kotlin code to be more expressive and maintainable, start using constructor references where they fit. It’s a simple change with big payoffs.
