接第一章:
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() 並且提供我們自己的實現來消除歧義。