Kotlin 中的繼承模式與 Java 有一些不一樣之處,主要在三個地方:java
Kotlin 中全部類的最終父類是 Any,而非 Java 中的 Object;app
Kotlin 中 非抽象類默認不可繼承;ide
Kotlin 中 非抽象類函數和類屬性默認不可覆蓋。函數
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 來覆蓋,這樣就能夠避免再次被子類覆蓋了。繼承
Kotlin 中沒有 extends 關鍵字,聲明一個類繼承自另外一個標記爲 open 的類的方法是:ci
class 子類[(主構造方法參數)]: 父類[(主構造方法參數)] {……}
能夠這樣理解,冒號 : 在 Kotlin 中表示前者屬於後者類型,好比咱們定義變量時用冒號聲明變量的類型,定義函數時用冒號聲明函數的返回類型。而在繼承中,子類實際上就屬於父類的類型,因此沒有必要再專門用一個 extends 關鍵字聲明父類,只須要用冒號。it
Java 中,子類在調用自身的構造方法前,會先調用父類的構造方法,因此若是父類有自定義的構造方法,子類就必須顯式地定義構造方法,並在構造方法中顯式調用父類的構造方法。Kotlin 也是如此,若是父類定義了主構造函數,子類就必須顯式地調用父類的主構造函數,緣由與 Java 同樣。
因此上面 Student 類在定義時,不能這樣寫:
// 編譯錯誤,Person 類有自定義的主構造函數class Student(name: String): Person {……}
在覆蓋屬性時,有兩點須要特別注意:
不容許用 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 方法
能夠在主構造函數中定義要覆蓋的屬性:
open class Person(open val name: String)class Student(override val name: String): Person(name)
如上,咱們在 Person 類的主構造函數中將 name 定義爲 open 的屬性,而後在 Student 類中使用 override 覆蓋了它。