Skip to main content

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:

  1. Primary Constructor
  2. 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

kotlin
class Person(val name: String, var age: Int)

In this simple example:

  • name is a read-only property (defined with val)
  • age is a mutable property (defined with var)
  • Both are automatically created as properties of the class

Example with Primary Constructor

kotlin
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:

kotlin
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

kotlin
class Person(val name: String) {
var age: Int = 0

constructor(name: String, age: Int) : this(name) {
this.age = age
}
}

Example with Secondary Constructor

kotlin
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:

kotlin
// 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

kotlin
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:

kotlin
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:

kotlin
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:

kotlin
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 or var
  • 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

  1. Create a Rectangle class with a primary constructor that takes width and height parameters. Add a method that calculates the area.

  2. Create a BankAccount class with a primary constructor for the account number and a secondary constructor that takes an initial balance.

  3. Create a Profile class with properties for name, bio, and profile picture URL. Use default arguments for optional fields.

  4. Extend the Todo application above to include a category field and a method to filter todos by category.

Additional Resources

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! :)