Kotlin Constructors
Constructors are special member functions that are called when an object of a class is created. They initialize the properties of a class and set up the object's initial state. In Kotlin, constructors are more flexible and powerful than in many other languages, offering a variety of ways to define how objects are created.
Understanding Constructors in Kotlin
Kotlin provides two types of constructors:
- Primary Constructor
- Secondary Constructor(s)
Additionally, Kotlin offers initialization blocks (init
) that work alongside constructors to initialize objects.
Let's explore each of these concepts in detail.
Primary Constructor
The primary constructor is part of the class header and appears right after the class name. It's a concise way to define a constructor and class properties in a single declaration.
Basic Syntax
class Person(val name: String, var age: Int)
In this simple example:
name
is a read-only property (defined withval
)age
is a mutable property (defined withvar
)- Both are automatically created as properties of the class
Example with Primary Constructor
class Student(val name: String, var grade: Int) {
fun displayInfo() {
println("Student: $name, Grade: $grade")
}
}
fun main() {
val student = Student("Alex", 10)
student.displayInfo()
}
Output:
Student: Alex, Grade: 10
Primary Constructor with Initialization Block
If you need to execute code during initialization, you can use the init
block:
class Employee(val name: String, val position: String) {
val fullDetails: String
init {
println("Initializing a new Employee")
fullDetails = "$name - $position"
}
}
fun main() {
val emp = Employee("Jane Doe", "Software Developer")
println(emp.fullDetails)
}
Output:
Initializing a new Employee
Jane Doe - Software Developer
You can have multiple init
blocks, and they execute in the order they appear in the class body, interspersed with property initializers.
Secondary Constructors
Secondary constructors provide alternative ways to create objects of a class. They are defined using the constructor
keyword.
Syntax
class Person(val name: String) {
var age: Int = 0
constructor(name: String, age: Int) : this(name) {
this.age = age
}
}
Example with Secondary Constructor
class User(val username: String) {
var email: String = ""
var isPremium: Boolean = false
constructor(username: String, email: String) : this(username) {
this.email = email
}
constructor(username: String, email: String, isPremium: Boolean) : this(username, email) {
this.isPremium = isPremium
}
fun displayInfo() {
println("Username: $username")
println("Email: ${if(email.isEmpty()) "Not provided" else email}")
println("Account type: ${if(isPremium) "Premium" else "Standard"}")
println("-----------------------")
}
}
fun main() {
val user1 = User("john_doe")
val user2 = User("jane_doe", "[email protected]")
val user3 = User("premium_user", "[email protected]", true)
user1.displayInfo()
user2.displayInfo()
user3.displayInfo()
}
Output:
Username: john_doe
Email: Not provided
Account type: Standard
-----------------------
Username: jane_doe
Email: [email protected]
Account type: Standard
-----------------------
Username: premium_user
Email: [email protected]
Account type: Premium
-----------------------
Constructor Parameters vs. Class Properties
It's important to understand the difference between constructor parameters and class properties:
// name and age are properties (accessible from outside the class)
class Person(val name: String, var age: Int)
// name and age are just parameters (not accessible outside the constructor)
class Contact(name: String, age: Int) {
val contactName = name // Creating a property from parameter
init {
println("Contact created for $name, age $age")
}
}
Example
class Book(title: String, val author: String) {
// title is just a parameter
// author is a property
val bookTitle = title // Converting parameter to property
fun displayInfo() {
println("'$bookTitle' by $author")
}
}
fun main() {
val book = Book("The Kotlin Programming Language", "JetBrains")
book.displayInfo()
// This works because author is a property
println("Author: ${book.author}")
// This works because we created bookTitle as a property
println("Title: ${book.bookTitle}")
// This would NOT work (if uncommented) because title is just a parameter
// println(book.title) // Error: Unresolved reference
}
Output:
'The Kotlin Programming Language' by JetBrains
Author: JetBrains
Title: The Kotlin Programming Language
Default Parameter Values
Kotlin constructors can have default values for parameters:
class Configuration(
val host: String = "localhost",
val port: Int = 8080,
val secure: Boolean = false
)
fun main() {
val config1 = Configuration()
val config2 = Configuration("api.example.com")
val config3 = Configuration("api.example.com", 443, true)
println("Config 1: ${config1.host}:${config1.port}, secure: ${config1.secure}")
println("Config 2: ${config2.host}:${config2.port}, secure: ${config2.secure}")
println("Config 3: ${config3.host}:${config3.port}, secure: ${config3.secure}")
}
Output:
Config 1: localhost:8080, secure: false
Config 2: api.example.com:8080, secure: false
Config 3: api.example.com:443, secure: true
Named Arguments with Constructors
You can use named arguments to make constructor calls more readable:
class Server(
val hostname: String,
val port: Int,
val timeout: Int,
val useSSL: Boolean
)
fun main() {
val server = Server(
hostname = "api.mycompany.com",
port = 8443,
timeout = 30000,
useSSL = true
)
println("Server: ${server.hostname}:${server.port}")
println("Timeout: ${server.timeout}ms")
println("SSL: ${server.useSSL}")
}
Output:
Server: api.mycompany.com:8443
Timeout: 30000ms
SSL: true
Practical Example: Building a Todo Application
Let's create a simple Todo application to demonstrate constructors in a real-world context:
class Todo(val id: Int, var title: String) {
var isCompleted: Boolean = false
var priority: Int = 0
var dueDate: String? = null
constructor(id: Int, title: String, priority: Int) : this(id, title) {
this.priority = priority
}
constructor(id: Int, title: String, dueDate: String) : this(id, title) {
this.dueDate = dueDate
}
constructor(id: Int, title: String, priority: Int, dueDate: String) : this(id, title) {
this.priority = priority
this.dueDate = dueDate
}
fun markAsCompleted() {
isCompleted = true
println("Task '$title' marked as completed!")
}
fun displayTodo() {
val status = if (isCompleted) "✓" else "□"
val priorityStr = when(priority) {
0 -> "Normal"
1 -> "Important"
2 -> "Urgent"
else -> "Unknown"
}
val dueDateStr = dueDate ?: "No due date"
println("[$status] #$id: $title (Priority: $priorityStr, Due: $dueDateStr)")
}
}
class TodoManager {
private val todos = mutableListOf<Todo>()
fun addTodo(todo: Todo) {
todos.add(todo)
println("Todo added: '${todo.title}'")
}
fun displayAllTodos() {
println("\n===== TODO LIST =====")
if (todos.isEmpty()) {
println("No todos found.")
} else {
todos.forEach { it.displayTodo() }
}
println("====================\n")
}
}
fun main() {
val manager = TodoManager()
// Using different constructors
manager.addTodo(Todo(1, "Buy groceries"))
manager.addTodo(Todo(2, "Finish Kotlin homework", 2))
manager.addTodo(Todo(3, "Call mom", "2023-09-15"))
manager.addTodo(Todo(4, "Prepare presentation", 1, "2023-09-10"))
manager.displayAllTodos()
// Mark a todo as completed
val todo = Todo(5, "Quick task")
todo.markAsCompleted()
manager.addTodo(todo)
manager.displayAllTodos()
}
Output:
Todo added: 'Buy groceries'
Todo added: 'Finish Kotlin homework'
Todo added: 'Call mom'
Todo added: 'Prepare presentation'
===== TODO LIST =====
[□] #1: Buy groceries (Priority: Normal, Due: No due date)
[□] #2: Finish Kotlin homework (Priority: Urgent, Due: No due date)
[□] #3: Call mom (Priority: Normal, Due: 2023-09-15)
[□] #4: Prepare presentation (Priority: Important, Due: 2023-09-10)
====================
Task 'Quick task' marked as completed!
Todo added: 'Quick task'
===== TODO LIST =====
[□] #1: Buy groceries (Priority: Normal, Due: No due date)
[□] #2: Finish Kotlin homework (Priority: Urgent, Due: No due date)
[□] #3: Call mom (Priority: Normal, Due: 2023-09-15)
[□] #4: Prepare presentation (Priority: Important, Due: 2023-09-10)
[✓] #5: Quick task (Priority: Normal, Due: No due date)
====================
Summary
Kotlin constructors provide flexible ways to initialize objects:
- Primary constructors are part of the class header and allow for concise property declarations
- Secondary constructors provide alternative ways to create objects with the
constructor
keyword - Init blocks execute initialization code in the order they appear in the class
- Constructor parameters can be turned into properties using
val
orvar
- Default parameter values and named arguments can make constructors more flexible and code more readable
Understanding constructors is crucial for designing clean and efficient Kotlin classes, as they determine how objects are created and initialized.
Exercises
-
Create a
Rectangle
class with a primary constructor that takeswidth
andheight
parameters. Add a method that calculates the area. -
Create a
BankAccount
class with a primary constructor for the account number and a secondary constructor that takes an initial balance. -
Create a
Profile
class with properties for name, bio, and profile picture URL. Use default arguments for optional fields. -
Extend the Todo application above to include a category field and a method to filter todos by category.
Additional Resources
- Official Kotlin Documentation on Classes and Objects
- Kotlin Classes and Constructors
- Advanced Kotlin: Object Initialization
Happy coding with Kotlin constructors!
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)