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
class Car constructor(color: String, brand: String, model: String) { var color: String = color // Mutable property val brand: String = brand // Immutable property val model: String = model // Immutable property}val myCar = Car("red", "Toyota", "Corolla") // Instance creationprintln("My car is a ${myCar.color} ${myCar.brand} ${myCar.model}.") // Prints "My car is a red Toyota Corolla."// Changing the color of myCarmyCar.color = "blue"println("After repainting, my car is a ${myCar.color} ${myCar.brand} ${myCar.model}.") // Prints "After repainting, my car is a blue Toyota Corolla."// Creating another instance for comparisonval myNeighboursCar = Car("blue", "Ford", "Mustang")println("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
// Primary constructor with properties directly definedclass Car(var color: String, val brand: String, val model: String)fun main() { val myCar = Car("red", "Toyota", "Corolla") // Instance creation println("My car is a ${myCar.color} ${myCar.brand} ${myCar.model}.") // Prints "My car is a red Toyota Corolla." // Demonstrating property mutability myCar.color = "blue" 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." }
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.
Join the 1M+ learners on CodeSignal
Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal
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:
class Car(var color: String) { var brand: String = "Unknown" var model: String = "Unknown" // First secondary constructor constructor(color: String, brand: String) : this(color) { this.brand = brand } // Second secondary constructor constructor(color: String, brand: String, model: String) : this(color, brand) { this.model = model }}fun main() { val car1 = Car("black") // Using the primary constructor to create a Car object val car2 = Car("red", "Toyota") // Using the first secondary constructor to create a Car object val car3 = Car("blue", "Toyota", "Corolla") // Using the second secondary constructor to create a Car object}
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
Decoding The `this` Keyword in Kotlin
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!
class Car(var color: String) { var brand: String = "Unknown" var model: String = "Unknown" // First secondary constructor constructor(color: String, brand: String) : this(color) { this.brand = brand } // Second secondary constructor constructor(color: String, brand: String, model: String) : this(color, brand) { this.model = model }}fun main() { val car1 = Car("black") // Using the primary constructor to create a Car object val car2 = Car("red", "Toyota") // Using the first secondary constructor to create a Car object val car3 = Car("blue", "Toyota", "Corolla") // Using the second secondary constructor to create a Car object}
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
class Car(var color: String, var brand: String) { init { println("First init block: Creating a $color $brand car.") } init { println("Second init block: The $color $brand car is ready.") }}val myCar = Car("red", "Toyota") // Executes both init blocks in order/* Output:First init block: Creating a red Toyota car.Second init block: The red Toyota car is ready.*/
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.
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
class Car(color: String, brand: String) { var color: String = color // Initializing class property with constructor parameter var brand: String = brand // Initializing class property with constructor parameter fun updateColor(newColor: String) { // 'this.color' refers to the class property, while 'newColor' is the method parameter this.color = newColor println("The color of this ${this.brand} car has been updated to ${this.color}.") }}fun main(){ val car1 = Car("red", "Toyota") val car2 = Car("red", "Honda") car1.updateColor("blue") // Prints: "The color of this Toyota car has been updated to blue." println(car1.color) // Prints: "blue" println(car2.color) // Prints: "red" }
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.