Lesson 2
Constructing the Basics: Kotlin Constructors and Initialization Explained
Understanding Primary Constructors

In Kotlin, a constructor is a special function for creating an instance of a class. The primary constructor is an integral part of the class declaration. It initializes the class with parameters passed to it when an object is created. Here's the full syntax for defining a class with a primary constructor and manually initializing its properties:

Kotlin
1class Car constructor(color: String, brand: String, model: String) { 2 var color: String = color // Mutable property 3 val brand: String = brand // Immutable property 4 val model: String = model // Immutable property 5} 6 7val myCar = Car("red", "Toyota", "Corolla") // Instance creation 8println("My car is a ${myCar.color} ${myCar.brand} ${myCar.model}.") // Prints "My car is a red Toyota Corolla." 9 10// Changing the color of myCar 11myCar.color = "blue" 12println("After repainting, my car is a ${myCar.color} ${myCar.brand} ${myCar.model}.") // Prints "After repainting, my car is a blue Toyota Corolla." 13 14// Creating another instance for comparison 15val myNeighboursCar = Car("blue", "Ford", "Mustang") 16println("My neighbour's car is a ${myNeighboursCar.color} ${myNeighboursCar.brand} ${myNeighboursCar.model}.") // Prints "My neighbour's car is a blue Ford Mustang."

In this detailed syntax, we define each property inside the class body and initialize them with the constructor parameters. The use of val for brand and model makes them immutable (read-only), while var for color allows it to be mutable.

Simplifying Property Declaration in Constructors

Kotlin allows for a more concise syntax to declare and initialize properties directly in the primary constructor. This shorthand notation automatically defines the properties with the constructor parameters, eliminating the need for manual property initialization:

Kotlin
1// Primary constructor with properties directly defined 2class Car(var color: String, val brand: String, val model: String) 3 4fun main() { 5 val myCar = Car("red", "Toyota", "Corolla") // Instance creation 6 println("My car is a ${myCar.color} ${myCar.brand} ${myCar.model}.") // Prints "My car is a red Toyota Corolla." 7 8 // Demonstrating property mutability 9 myCar.color = "blue" 10 println("After a color change, my car is a ${myCar.color} ${myCar.brand} ${myCar.model}.") // Prints "After a color change, my car is a blue Toyota Corolla." 11}

This version clearly shows how Kotlin's concise constructor syntax can define and initialize both mutable (var) and immutable (val) properties directly. This approach significantly reduces boilerplate, making your code more readable. Properties declared as val are immutable, meaning once assigned, their values cannot be changed, while var properties are mutable and can be modified.

Understanding Secondary Constructors

Kotlin supports secondary constructors for additional initialization flexibility, complementing the primary constructor. These constructors allow method overloading and can provide default arguments. Each secondary constructor must delegate to the primary constructor using the this keyword, ensuring consistent initial setup.

The this keyword in secondary constructors serves a crucial role. It explicitly calls the primary constructor, passing necessary parameters. This ensures that any initialization logic in the primary constructor is executed before the secondary constructor's own initialization code runs. This delegation mechanism maintains a uniform initialization sequence across all constructors.

Example:

Kotlin
1class Car(var color: String) { 2 var brand: String = "Unknown" 3 var model: String = "Unknown" 4 5 // First secondary constructor 6 constructor(color: String, brand: String) : this(color) { 7 this.brand = brand 8 } 9 10 // Second secondary constructor 11 constructor(color: String, brand: String, model: String) : this(color, brand) { 12 this.model = model 13 } 14} 15 16fun main() { 17 val car1 = Car("black") // Using the primary constructor to create a Car object 18 val car2 = Car("red", "Toyota") // Using the first secondary constructor to create a Car object 19 val car3 = Car("blue", "Toyota", "Corolla") // Using the second secondary constructor to create a Car object 20}

In this example, the Car class has one primary constructor and two secondary constructors. The first secondary constructor allows initializing the color and brand, while the second also initializes the model. Through the use of this, both secondary constructors ensure the color property is initialized by the primary constructor, aligning with Kotlin's approach to constructor delegation and overloading.

Working With `init` Blocks

Initialization blocks (init blocks) execute in the sequence they appear within the class, directly following the initialization of the primary constructor's parameters. They are ideal for setting up initial state or conducting validation. Notably, init blocks run before any secondary constructor’s body, highlighting the primary constructor's paramount role in object initialization.

Kotlin
1class Car(var color: String, var brand: String) { 2 init { 3 println("First init block: Creating a $color $brand car.") 4 } 5 6 init { 7 println("Second init block: The $color $brand car is ready.") 8 } 9} 10 11val myCar = Car("red", "Toyota") // Executes both init blocks in order 12 13/* Output: 14First init block: Creating a red Toyota car. 15Second init block: The red Toyota car is ready. 16*/

This demonstrates that upon instantiating Car("red", "Toyota"), Kotlin initializes the color and brand properties. Following this, it executes each init block in the order they are declared within the class. This ensures all properties are properly set before executing any setup or validation logic contained in init blocks.

Decoding The `this` Keyword in Kotlin

The this keyword in Kotlin is a reference to the current object — a way to access the class members from within the class itself. It becomes particularly handy in disambiguating between class properties and constructor parameters when they share the same name. To give a practical demonstration, let's enrich our example by including a method that updates the car's properties, further showcasing how this is used to refer to the instance members.

Kotlin
1class Car(color: String, brand: String) { 2 var color: String = color // Initializing class property with constructor parameter 3 var brand: String = brand // Initializing class property with constructor parameter 4 5 fun updateColor(newColor: String) { 6 // 'this.color' refers to the class property, while 'newColor' is the method parameter 7 this.color = newColor 8 println("The color of this ${this.brand} car has been updated to ${this.color}.") 9 } 10} 11 12fun main(){ 13 val car1 = Car("red", "Toyota") 14 val car2 = Car("red", "Honda") 15 car1.updateColor("blue") // Prints: "The color of this Toyota car has been updated to blue." 16 println(car1.color) // Prints: "blue" 17 println(car2.color) // Prints: "red" 18}

In this example, the constructor initializes the class properties with the provided parameters. The updateColor method then uses this to clarify that color refers to the car's color property, not the newColor parameter. This usage of this ensures that Kotlin correctly understands which color is being referred to, preventing any ambiguity between local variables or parameters and class properties. This practical example underscores the utility of this in real-world coding scenarios, making it clear how it helps maintain clarity and correctness in your code.

Recap And Next Steps

Congratulations! You've grasped key concepts—constructor, property declaration, initialization logic, init block, and this keyword. Be prepared for exercises to solidify your understanding and get ready for a deeper dive into Kotlin's Object-Oriented Programming. See you there!

Enjoy this lesson? Now it's time to practice with Cosmo!
Practice is how you turn knowledge into actual skills.