Skip to Content

Constructor trong Kotlin

🎯 Mục tiêu: Hiểu Primary Constructor, Secondary Constructor, và init blocks - cách khởi tạo object trong Kotlin.


💡 Constructor là gì?

Constructor là hàm đặc biệt được gọi khi tạo object. Kotlin có hai loại:

  • Primary Constructor: Khai báo ngay sau tên class
  • Secondary Constructor: Khai báo trong body class với keyword constructor
// Primary constructor class User(val name: String, var age: Int) // Tạo object val user = User("Alice", 25)

📝 Primary Constructor

Cú pháp cơ bản

// val/var tự động tạo property class User(val name: String, var email: String) // Equivalent verbose version class User { val name: String var email: String constructor(name: String, email: String) { this.name = name this.email = email } }

Parameters vs Properties

class Example( val property: String, // ✅ Tạo property (có getter) var mutableProp: String, // ✅ Tạo property (getter + setter) parameter: String // ❌ Chỉ là parameter, không tạo property ) { // parameter chỉ available trong init block init { println("Parameter: $parameter") } // ❌ Không thể access parameter ở đây // fun printParam() = println(parameter) }
💡

Quy tắc: Thêm val/var trước parameter để tự động tạo property. Không có val/var = chỉ là parameter cho constructor.

Default Values

class User( val name: String, val email: String = "", val age: Int = 0, val isActive: Boolean = true ) fun main() { val user1 = User("Alice") // Dùng default values val user2 = User("Bob", "[email protected]") // Override một số val user3 = User("Charlie", age = 30) // Named arguments val user4 = User( name = "David", email = "[email protected]", age = 25, isActive = false ) }

Visibility Modifier cho Constructor

// Private constructor - cấm tạo object từ bên ngoài class Singleton private constructor(val name: String) { companion object { val instance = Singleton("Default") } } // Internal constructor class InternalClass internal constructor(val data: String) fun main() { // val s = Singleton("Test") // ❌ Lỗi! Private constructor val instance = Singleton.instance // ✅ OK }

🔄 init Blocks

Kotlin không cho phép code trực tiếp trong primary constructor. Dùng init block:

class User(val name: String, val email: String) { // init block chạy ngay sau primary constructor init { println("Creating user: $name") require(name.isNotBlank()) { "Name cannot be blank" } require(email.contains("@")) { "Invalid email" } } // Property initialization val normalizedName = name.trim().lowercase() // Có thể có nhiều init blocks - chạy theo thứ tự khai báo init { println("Validation passed!") } } fun main() { val user = User("Alice", "[email protected]") // Output: // Creating user: Alice // Validation passed! }

Thứ tự khởi tạo

class Demo(val param: String) { // 1. Property initializers và init blocks chạy theo thứ tự khai báo val first = println("1. First property").let { "first" } init { println("2. First init block") } val second = println("3. Second property").let { "second" } init { println("4. Second init block") } } fun main() { Demo("test") // 1. First property // 2. First init block // 3. Second property // 4. Second init block }

🔧 Secondary Constructor

Khi cần nhiều cách để tạo object:

class User(val name: String, val email: String, val age: Int) { // Secondary constructor PHẢI delegate cho primary constructor constructor(name: String) : this(name, "$name@example.com", 0) { println("Created with name only") } constructor(name: String, email: String) : this(name, email, 0) { println("Created with name and email") } // init block vẫn chạy trước secondary constructor body init { println("Init block runs first") } } fun main() { val user1 = User("Alice") // Init block runs first // Created with name only println("Name: ${user1.name}, Email: ${user1.email}") // Name: Alice, Email: [email protected] }

Delegation Chain

class Person(val name: String, val age: Int, val city: String) { constructor(name: String, age: Int) : this(name, age, "Unknown") constructor(name: String) : this(name, 0) // delegate to above constructor constructor() : this("Guest") // delegate to above } fun main() { val p1 = Person() // Guest, 0, Unknown val p2 = Person("Alice") // Alice, 0, Unknown val p3 = Person("Bob", 25) // Bob, 25, Unknown val p4 = Person("Charlie", 30, "NYC") // Charlie, 30, NYC }
⚠️

Best Practice: Ưu tiên default parameters thay vì secondary constructors:

// ✅ Better: Default parameters class User( val name: String = "Guest", val email: String = "", val age: Int = 0 ) // ❌ Verbose: Secondary constructors class User(val name: String, val email: String, val age: Int) { constructor(name: String) : this(name, "", 0) constructor(name: String, email: String) : this(name, email, 0) }

🎯 Validation trong Constructor

class BankAccount( val accountNumber: String, initialBalance: Double = 0.0 ) { var balance: Double = 0.0 private set init { require(accountNumber.length == 10) { "Account number must be 10 digits" } require(initialBalance >= 0) { "Initial balance cannot be negative" } balance = initialBalance } } fun main() { val account = BankAccount("1234567890", 1000.0) println(account.balance) // 1000.0 // ❌ IllegalArgumentException: Account number must be 10 digits // val invalid = BankAccount("123", 100.0) }

🛠️ Thực hành

Bài tập: Tạo class với multiple constructors

// TODO: Tạo class Product với: // - name (required) // - price (required) // - quantity (default = 0) // - category (default = "General") // - totalValue computed property // - Validation: price > 0, quantity >= 0

Lời giải:

class Product( val name: String, val price: Double, val quantity: Int = 0, val category: String = "General" ) { val totalValue: Double get() = price * quantity init { require(name.isNotBlank()) { "Name cannot be blank" } require(price > 0) { "Price must be positive" } require(quantity >= 0) { "Quantity cannot be negative" } } override fun toString(): String { return "$name ($category): $${"%.2f".format(price)} x $quantity = $${"%.2f".format(totalValue)}" } } fun main() { val laptop = Product("MacBook Pro", 2499.99, 5, "Electronics") val book = Product("Kotlin Guide", 49.99) println(laptop) // MacBook Pro (Electronics): $2499.99 x 5 = $12499.95 println(book) // Kotlin Guide (General): $49.99 x 0 = $0.00 }

📱 Trong Android

// Custom View với multiple constructors class CustomButton : AppCompatButton { // Default constructor cho code constructor(context: Context) : super(context) { init(null) } // Constructor cho XML inflation constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) { init(attrs) } // Constructor với style constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { init(attrs) } private fun init(attrs: AttributeSet?) { // Initialize custom attributes } } // ViewModel without default constructor class UserViewModel( private val repository: UserRepository, private val dispatcher: CoroutineDispatcher = Dispatchers.IO ) : ViewModel() { // ViewModelFactory required } // Data class for network response data class ApiUser( val id: Long, val name: String, val email: String = "", val avatarUrl: String? = null )

⚠️ Lưu ý quan trọng

💡

Thứ tự thực thi khi tạo object:

  1. Primary constructor arguments evaluated
  2. Superclass constructor (nếu có)
  3. Property initializers và init blocks (theo thứ tự khai báo)
  4. Secondary constructor body (nếu dùng)
⚠️

Tránh dùng this trong init block trước khi fully initialized:

class Problematic(val data: String) { val length = data.length // ✅ OK init { registerSelf() // ⚠️ Nguy hiểm: object chưa fully initialized } fun registerSelf() { /* ... */ } }

✅ Checklist - Tự kiểm tra

Sau bài học này, bạn có thể:

  • Tạo class với primary constructor
  • Phân biệt val/var parameter (property) vs không có modifier (parameter only)
  • Sử dụng default values cho constructor parameters
  • Sử dụng init blocks cho initialization logic
  • Hiểu thứ tự thực thi của init blocks
  • Tạo secondary constructors với delegation
  • Thực hiện validation trong constructor

Tiếp theo: Visibility Modifiers

Last updated on