面向對象:繼承

Kotlin 中的繼承模式與 Java 有一些不一樣之處,主要在三個地方:java

  1. Kotlin 中全部類的最終父類是 Any,而非 Java 中的 Object;app

  2. Kotlin 中 非抽象類默認不可繼承ide

  3. Kotlin 中 非抽象類函數和類屬性默認不可覆蓋函數

1. open 關鍵字和 override 關鍵字

open 關鍵字在 Kotlin 中能夠用在定義 非抽象的類、類函數和類屬性 以前,用來將它們標記爲可繼承 的:spa

open class Person(val name: String) {
    open var age = 0
    open fun say() = "My name is $name, $age years old."}

override 關鍵字用在定義 子類覆蓋父類函數和屬性 以前,用來標記覆蓋了父類的函數和屬性:orm

class Student(name: String) : Person(name) {
    override var age = 0
    override fun say() = "I'm a student named $name, $age years old."}

須要注意的是,若是類不是 open 的,那麼它內部的屬性和函數都不能是 open 的。理由很簡單,不會被繼承的類,不可能有屬性和函數被覆蓋。對象

雖然 Kotlin 默認沒有 open 的屬性和函數不能被覆蓋,但實際上使用 override 標記和屬性和函數能夠被覆蓋,若是一個 open 類不想讓子類覆蓋本身 override 的屬性和函數,可使用 final override 來覆蓋,這樣就能夠避免再次被子類覆蓋了。繼承

2. 繼承類的寫法和構造方法

Kotlin 中沒有 extends 關鍵字,聲明一個類繼承自另外一個標記爲 open 的類的方法是:ci

class 子類[(主構造方法參數)]: 父類[(主構造方法參數)] {……}

能夠這樣理解,冒號 : 在 Kotlin 中表示前者屬於後者類型,好比咱們定義變量時用冒號聲明變量的類型,定義函數時用冒號聲明函數的返回類型。而在繼承中,子類實際上就屬於父類的類型,因此沒有必要再專門用一個 extends 關鍵字聲明父類,只須要用冒號。it

Java 中,子類在調用自身的構造方法前,會先調用父類的構造方法,因此若是父類有自定義的構造方法,子類就必須顯式地定義構造方法,並在構造方法中顯式調用父類的構造方法。Kotlin 也是如此,若是父類定義了主構造函數,子類就必須顯式地調用父類的主構造函數,緣由與 Java 同樣。

因此上面 Student 類在定義時,不能這樣寫:

// 編譯錯誤,Person 類有自定義的主構造函數class Student(name: String): Person {……}

3. 覆蓋屬性時的細節

在覆蓋屬性時,有兩點須要特別注意:

  1. 不容許用 val 屬性覆蓋 var 屬性。用 val 屬性覆蓋 val 屬性和用 var 屬性覆蓋 var 屬性很好理解,該是什麼樣仍是什麼樣嘛。此外,Kotlin 還容許用 var 屬性覆蓋 val 屬性(只需給子類中的屬性添加一個 setter 方法),但不容許用 val 屬性覆蓋 var 屬性:

    open class Person() {
      var name = ""}class Student(): Person(name) {
      override val name: String = name}

    當咱們使用多態時:

    val alex: Person = Student()alex.name = "Alex" // 錯誤,子類對象的屬性沒有 setter 方法

  2. 能夠在主構造函數中定義要覆蓋的屬性

    open class Person(open val name: String)class Student(override val name: String): Person(name)

    如上,咱們在 Person 類的主構造函數中將 name 定義爲 open 的屬性,而後在 Student 類中使用 override 覆蓋了它。

相關文章
相關標籤/搜索