Kotlin constructors are special member functions that initialize class instances when objects are created. Unlike Java, Kotlin provides two distinct types of constructors: primary constructors and secondary constructors. The primary constructor is declared in the class header, while secondary constructors are defined within the class body using the constructor
keyword.
Every Kotlin class can have one primary constructor and multiple secondary constructors. The primary constructor cannot contain executable code, but you can use init blocks to execute initialization logic. This design makes Kotlin constructors more structured and predictable than their Java counterparts.
The Kotlin primary constructor is the most concise way to initialize class properties. It’s declared immediately after the class name and becomes part of the class header. Here’s the basic syntax for a primary constructor:
class ClassName constructor(parameter1: Type, parameter2: Type) {
// class body
}
You can omit the constructor
keyword when there are no annotations or visibility modifiers:
class Person(val name: String, var age: Int) {
// class body
}
When you declare parameters in a primary constructor, you can simultaneously declare class properties by using val
or var
keywords:
val
creates a read-only property (immutable)var
creates a mutable propertyclass Student(val studentId: String, var grade: Double) {
// studentId is read-only, grade is mutable
}
Kotlin constructors support default parameters, eliminating the need for multiple constructor overloads:
class GameCharacter(
val name: String = "Anonymous",
var level: Int = 1,
var health: Double = 100.0
) {
// Properties with default values
}
This allows flexible object creation:
val character1 = GameCharacter() // Uses all defaults
val character2 = GameCharacter("Warrior") // Custom name, default level and health
val character3 = GameCharacter("Mage", 5) // Custom name and level
val character4 = GameCharacter("Archer", 3, 85.5) // All custom values
Since primary constructors cannot contain executable code, Kotlin provides init blocks for initialization logic. Init blocks are executed when the class instance is created and have access to primary constructor parameters:
class Vehicle(val brand: String, val model: String) {
val vehicleInfo: String
init {
println("Creating vehicle: $brand $model")
vehicleInfo = "$brand $model".uppercase()
// Validation logic
require(brand.isNotEmpty()) { "Brand cannot be empty" }
require(model.isNotEmpty()) { "Model cannot be empty" }
}
}
A class can have multiple init blocks, and they execute in the order they appear:
class DatabaseConnection(val host: String, val port: Int) {
val connectionString: String
var isConnected: Boolean = false
init {
println("Initializing database connection...")
connectionString = "jdbc:postgresql://$host:$port/database"
}
init {
println("Validating connection parameters...")
require(port in 1..65535) { "Invalid port number: $port" }
}
init {
println("Establishing connection...")
isConnected = true
println("Connection established successfully!")
}
}
Secondary constructors provide alternative ways to initialize objects when the primary constructor isn’t sufficient. They’re declared using the constructor
keyword and must delegate to the primary constructor using the this
keyword:
class Rectangle(val width: Double, val height: Double) {
val area: Double = width * height
// Secondary constructor for square
constructor(side: Double) : this(side, side) {
println("Creating square with side: $side")
}
// Secondary constructor with default dimensions
constructor() : this(1.0, 1.0) {
println("Creating unit rectangle")
}
}
Every secondary constructor must directly or indirectly delegate to the primary constructor:
class BankAccount(val accountNumber: String, var balance: Double) {
val accountType: String
init {
accountType = if (balance >= 10000) "Premium" else "Standard"
println("Account created: $accountNumber, Type: $accountType")
}
// Secondary constructor with default balance
constructor(accountNumber: String) : this(accountNumber, 0.0) {
println("Account opened with zero balance")
}
// Secondary constructor delegating to another secondary constructor
constructor() : this("AUTO-${System.currentTimeMillis()}") {
println("Auto-generated account number")
}
}
You can control constructor visibility using access modifiers:
class SecureData private constructor(val data: String) {
companion object {
fun createSecureData(input: String): SecureData? {
return if (input.length >= 8) {
SecureData(input.reversed())
} else {
null
}
}
}
}
When using annotations or visibility modifiers, the constructor
keyword becomes mandatory:
class ApiClient @Inject constructor(
private val httpClient: HttpClient,
private val apiKey: String
) {
// class implementation
}
You can initialize properties that aren’t declared in the primary constructor:
class MusicPlayer(val brand: String) {
val supportedFormats: List<String>
var volume: Int
var isPlaying: Boolean
init {
supportedFormats = listOf("MP3", "FLAC", "WAV", "AAC")
volume = 50
isPlaying = false
println("$brand music player initialized")
}
}
Here’s a comprehensive example demonstrating all Kotlin constructor concepts:
// Import statements (if needed)
import java.time.LocalDateTime
import java.util.*
class Product(
val id: String,
val name: String,
var price: Double,
val category: String = "General"
) {
val productCode: String
var stock: Int = 0
var isAvailable: Boolean = false
val createdAt: LocalDateTime
val tags: MutableList<String> = mutableListOf()
init {
// Generate product code
productCode = "${category.uppercase()}-${id}"
createdAt = LocalDateTime.now()
// Validate price
require(price >= 0) { "Price cannot be negative: $price" }
require(name.isNotBlank()) { "Product name cannot be blank" }
println("Product created: $productCode - $name")
}
init {
// Set default availability based on category
isAvailable = category != "Restricted"
println("Product availability set to: $isAvailable")
}
// Secondary constructor for discounted products
constructor(
id: String,
name: String,
originalPrice: Double,
discountPercent: Double,
category: String
) : this(id, name, originalPrice * (1 - discountPercent / 100), category) {
tags.add("Discounted")
println("Discounted product created with ${discountPercent}% off")
}
// Secondary constructor for limited edition products
constructor(
id: String,
name: String,
price: Double,
category: String,
limitedStock: Int
) : this(id, name, price, category) {
stock = limitedStock
tags.addAll(listOf("Limited Edition", "Exclusive"))
println("Limited edition product created with stock: $limitedStock")
}
// Method to display product information
fun displayInfo() {
println("""
Product Information:
Code: $productCode
Name: $name
Price: $${String.format("%.2f", price)}
Category: $category
Stock: $stock
Available: $isAvailable
Tags: ${tags.joinToString(", ")}
Created: $createdAt
""".trimIndent())
}
// Method to update stock
fun updateStock(newStock: Int) {
require(newStock >= 0) { "Stock cannot be negative" }
stock = newStock
isAvailable = stock > 0
println("Stock updated to: $stock, Available: $isAvailable")
}
// Method to add tags
fun addTag(tag: String) {
if (!tags.contains(tag)) {
tags.add(tag)
println("Tag added: $tag")
}
}
}
// Demonstration function
fun main() {
println("=== Kotlin Constructors Demo ===\n")
// Using primary constructor with default parameter
val laptop = Product("LAP001", "Gaming Laptop", 1299.99)
laptop.updateStock(15)
laptop.addTag("Gaming")
laptop.displayInfo()
println("\n" + "=".repeat(50) + "\n")
// Using secondary constructor for discounted product
val smartphone = Product("PHN001", "Flagship Phone", 999.99, 15.0, "Electronics")
smartphone.updateStock(25)
smartphone.displayInfo()
println("\n" + "=".repeat(50) + "\n")
// Using secondary constructor for limited edition
val collectible = Product("COL001", "Vintage Watch", 2499.99, "Luxury", 5)
collectible.displayInfo()
println("\n" + "=".repeat(50) + "\n")
// Creating products with different parameter combinations
val basicProduct = Product("BAS001", "Office Chair", 199.99, "Furniture")
basicProduct.updateStock(50)
val premiumProduct = Product("PRE001", "Executive Desk", 899.99, "Premium")
premiumProduct.updateStock(10)
premiumProduct.addTag("Premium Quality")
println("Basic Product: ${basicProduct.name} - ${basicProduct.productCode}")
println("Premium Product: ${premiumProduct.name} - ${premiumProduct.productCode}")
}
Expected Output:
=== Kotlin Constructors Demo ===
Product created: GENERAL-LAP001 - Gaming Laptop
Product availability set to: true
Stock updated to: 15, Available: true
Tag added: Gaming
Product Information:
Code: GENERAL-LAP001
Name: Gaming Laptop
Price: $1299.99
Category: General
Stock: 15
Available: true
Tags: Gaming
Created: 2025-06-20T10:30:45.123
==================================================
Product created: ELECTRONICS-PHN001 - Flagship Phone
Product availability set to: true
Discounted product created with 15.0% off
Stock updated to: 25, Available: true
Product Information:
Code: ELECTRONICS-PHN001
Name: Flagship Phone
Price: $849.99
Category: Electronics
Stock: 25
Available: true
Tags: Discounted
Created: 2025-06-20T10:30:45.156
==================================================
Product created: LUXURY-COL001 - Vintage Watch
Product availability set to: true
Limited edition product created with stock: 5
Product Information:
Code: LUXURY-COL001
Name: Vintage Watch
Price: $2499.99
Category: Luxury
Stock: 5
Available: true
Tags: Limited Edition, Exclusive
Created: 2025-06-20T10:30:45.189
==================================================
Product created: FURNITURE-BAS001 - Office Chair
Product availability set to: true
Stock updated to: 50, Available: true
Product created: PREMIUM-PRE001 - Executive Desk
Product availability set to: true
Stock updated to: 10, Available: true
Tag added: Premium Quality
Basic Product: Office Chair - FURNITURE-BAS001
Premium Product: Executive Desk - PREMIUM-PRE001