🏁 Constructor
🎯 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ì?
Phần tiêu đề “💡 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 constructorclass User(val name: String, var age: Int)
// Tạo objectval user = User("Alice", 25)📝 Primary Constructor
Phần tiêu đề “📝 Primary Constructor”Cú pháp cơ bản
Phần tiêu đề “Cú pháp cơ bản”// val/var tự động tạo propertyclass User(val name: String, var email: String)
// Equivalent verbose versionclass User { val name: String var email: String
constructor(name: String, email: String) { this.name = name this.email = email }}Parameters vs Properties
Phần tiêu đề “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)}Default Values
Phần tiêu đề “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 user3 = User("Charlie", age = 30) // Named arguments val user4 = User( name = "David", age = 25, isActive = false )}Visibility Modifier cho Constructor
Phần tiêu đề “Visibility Modifier cho Constructor”// Private constructor - cấm tạo object từ bên ngoàiclass Singleton private constructor(val name: String) { companion object { val instance = Singleton("Default") }}
// Internal constructorclass InternalClass internal constructor(val data: String)
fun main() { // val s = Singleton("Test") // ❌ Lỗi! Private constructor val instance = Singleton.instance // ✅ OK}🔄 init Blocks
Phần tiêu đề “🔄 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() { // Output: // Creating user: Alice // Validation passed!}Thứ tự khởi tạo
Phần tiêu đề “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
Phần tiêu đề “🔧 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
Phần tiêu đề “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}🎯 Validation trong Constructor
Phần tiêu đề “🎯 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
Phần tiêu đề “🛠️ 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 >= 0Lờ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
Phần tiêu đề “📱 Trong Android”// Custom View với multiple constructorsclass 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 constructorclass UserViewModel( private val repository: UserRepository, private val dispatcher: CoroutineDispatcher = Dispatchers.IO) : ViewModel() { // ViewModelFactory required}
// Data class for network responsedata class ApiUser( val id: Long, val name: String, val email: String = "", val avatarUrl: String? = null)⚠️ Lưu ý quan trọng
Phần tiêu đề “⚠️ Lưu ý quan trọng”✅ Checklist - Tự kiểm tra
Phần tiêu đề “✅ 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/varparameter (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