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:
Kotlin1class 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.
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:
Kotlin1// 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.
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:
Kotlin1class 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.
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.
Kotlin1class 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.
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.
Kotlin1class 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.
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!