Kotlin 類別定義 (Class)

Kotlin 的類別定義非常簡潔。

定義類別

class Person { /* ... */ }

如果沒有內容,連大括號都可以省掉:

class Empty

建構函式 (Constructor)

這是與 Java 差別最大的地方。Kotlin 有 Primary Constructor (主建構子)Secondary Constructor (次建構子)

Primary Constructor

直接寫在類別名稱後面。這是最推薦的寫法。

// 定義類別同時定義屬性 (val name)
class Person(val name: String, var age: Int)

fun main() {
    val p = Person("Miko", 18)
    println(p.name)
}

看到那個 val 了嗎?它自動幫你宣告了屬性、並在建構時賦值。一行抵 Java 十行。

初始化區塊 (init)

如果你在建構時需要執行一些邏輯 (例如驗證資料),可以寫在 init 區塊。

class Person(val name: String, var age: Int) {
    init {
        println("User $name created!")
        if (age < 0) age = 0
    }
}
    }
}

建構子預設值 (Default Arguments)

在 Kotlin 中,不需要像 Java 那樣宣告一大堆 Overloading 的建構函式。你只需要為參數提供預設值即可。

// 如果不傳 age,預設為 0
class Person(val name: String, var age: Int = 0)

fun main() {
    val p1 = Person("Miko")      // age = 0
    val p2 = Person("John", 20)  // age = 20
    
    // 甚至可以使用具名參數 (Named Arguments) 來指定特定參數
    val p3 = Person(age = 18, name = "Amy") 
}

這大大減少了對 Secondary Constructor 的依賴。除非你需要相容 Java 的呼叫,否則盡量使用預設參數。

Secondary Constructor

如果需要多種建構方式,可以使用 constructor 關鍵字。

但注意:如果有定義 Primary Constructor,Secondary Constructor 必須呼叫它 (this(...))。即使用次建構子時,Kotlin 要求你必須先初始化主建構子所定義的屬性。

// 主建構子 (Primary Constructor)
class Person(val name: String) {
    var age: Int = 0

    // 次建構子 (Secondary Constructor)
    // 使用 this() 呼叫主建構子,並初始化 name
    constructor(name: String, age: Int) : this(name) {
        this.age = age
    }
}

建立物件

Kotlin 建立物件 不需要 new 關鍵字

val p = Person("Miko", 18)

"this" 關鍵字

this 關鍵字代表 當前的物件實體 (Current Receiver)。它的用途主要有:

1. 存取當前類別的成員

當函式參數名稱與屬性名稱發生衝突時,使用 this 來區分。

class Person(name: String) {
    var name: String = ""
    
    init {
        // this.name 是屬性,name 是參數
        this.name = name 
    }
}

2. 存取外部類別 (Qualified this)

在內部類別 (Inner Class) 或巢狀結構中,如果名稱衝突,我們需要指名要存取哪一層的 this。 語法是 this@Label,其中 Label 通常是類別名稱。

class Outer {
    val a = 10
    
    inner class Inner {
        val a = 20
        
        fun test() {
            val a = 30
            
            println(a)           // 30 (區域變數)
            println(this.a)      // 20 (Inner 的屬性)
            println(this@Outer.a) // 10 (Outer 的屬性)
        }
    }
}

3. 在擴充函式中

擴充函式中,this 代表被擴充的那個物件實體。

fun String.printMe() {
    // 這裡的 this 就是呼叫 printMe 的那個字串
    println(this)
}

"Hello".printMe() // 印出 Hello