Kotlin學習第二章——類與對象

接第一章:
Kotlin學習第一篇——基礎知識
類與對象
在Kotlin中,類構造函數有主構造函數與次構造函數之分,創建類實例的時候,沒有new關鍵字,只需像普通函數一樣調用構造函數即可。

主構造函數是類頭的一部分:它跟在類名(與可選的類型參數)後
如:class Person constructor(firstName: String) { … }

主構造函數不能包含任何的代碼。初始化的代碼可以放到以 init 關鍵字作爲前綴的初始化塊(initializer blocks)中。

class InitOrderDemo(name: String) {
    val firstProperty = "First property: $name".also(::println)
    
    init {
        println("First initializer block that prints ${name}")
    }
    
    val secondProperty = "Second property: ${name.length}".also(::println)
    
    init {
        println("Second initializer block that prints ${name.length}")
    }
}

在實例初始化期間,初始化塊按照它們出現在類體中的順序執行,與屬性初始化器交織在一起

如果類有一個主構造函數,每個次構造函數需要委託給主構造函數, 可以直接委託或者通過別的次構造函數間接委託。委託到同一個類的另一個構造函數用 this 關鍵字即可:

class Person(val name: String) {
    constructor(name: String, parent: Person) : this(name) {
        parent.children.add(this)
    }
}

初始化塊中的代碼實際上會成爲主構造函數的一部分。委託給主構造函數會作爲次構造函數的第一條語句,因此所有初始化塊中的代碼都會在次構造函數體之前執行。即使該類沒有主構造函數,這種委託仍會隱式發生,並且仍會執行初始化塊

class Constructors {
    init {
        println("Init block")
    }

    constructor(i: Int) {
        println("Constructor")
    }
}
// 輸出:Init block Constructor

關於類的繼承
在 Kotlin 中所有類都有一個共同的超類 Any。Any 並不是 java.lang.Object;尤其是,它除了 equals()、hashCode() 與 toString() 外沒有任何成員。
Kotlin中覆蓋方法與java中的方法重寫類似,但也有些許差別:

open class Base {
    open fun v() { ... }
    fun nv() { ... }
}
class Derived() : Base() {
    override fun v() { ... }
}

Kotlin中可覆蓋的成員稱爲“開放”上文中的“open”,覆蓋後的成員需要顯示的修飾符,如:上文的“override”。
標記爲 override 的成員本身是開放的,也就是說,它可以在子類中覆蓋。如果你想禁止再次覆蓋,使用 final 關鍵字:

open class AnotherDerived() : Base() {
    final override fun v() { ... }
}

open class Base(val name: String) {

    init { println("Initializing Base") }

    open val size: Int = 
        name.length.also { println("Initializing size in Base: $it") }
}

class Derived(
    name: String,
    val lastName: String
) : Base(name.capitalize().also { println("Argument for Base: $it") }) {

    init { println("Initializing Derived") }
    override val size: Int =
        (super.size + lastName.length).also { println("Initializing size in Derived: $it") }
}

fun main() {
    println("Constructing Derived(\"hello\", \"world\")")
    val d = Derived("hello", "world")
}

輸出順序爲:
Constructing Derived(“hello”, “world”)
Argument for Base: hello
Initializing Base Initializing size in Base: 5
Initializing Derived
Initializing size in Derived: 10

注:基類構造函數執行時,派生類中聲明或覆蓋的屬性都還沒有初始化。如果在基類初始化邏輯中(直接或通過另一個覆蓋的 open 成員的實現間接)使用了任何一個這種屬性,那麼都可能導致不正確的行爲或運行時故障。設計一個基類時,應該避免在構造函數、屬性初始化器以及 init 塊中使用 open 成員

綜述:在 Kotlin 中,實現繼承由下述規則規定:如果一個類從它的直接超類繼承相同成員的多個實現, 它必須覆蓋這個成員並提供其自己的實現(也許用繼承來的其中之一)。 爲了表示採用從哪個超類型繼承的實現,我們使用由尖括號中超類型名限定的 super,如 super < Base >:

open class A {
    open fun f() { print("A") }
    fun a() { print("a") }
}

interface B {
    fun f() { print("B") } // 接口成員默認就是“open”的
    fun b() { print("b") }
}

class C() : A(), B {
    // 編譯器要求覆蓋 f():
    override fun f() {
        super<A>.f() // 調用 A.f()
        super<B>.f() // 調用 B.f()
  }
}

同時繼承 A 與 B 沒問題,並且 a() 與 b() 也沒問題因爲 C 只繼承了每個函數的一個實現。 但是 f() 由 C繼承了兩個實現,所以我們必須在 C 中覆蓋 f() 並且提供我們自己的實現來消除歧義。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章