Kotlin 的構造函數,以及類的繼承,和 Java 相比,在使用上還有些差別的,一些寫法并不是很好理解,這里簡單的分析記錄下。
一、類、對象
在學習 Kotlin 構造函數、繼承之前,先簡單了解在 Kotlin 中如何定義類、創建對象。
class Person {
var name: String = ""
var age: Int = 0
}
和 Java 一樣,在 Kotlin 中也是用class
關鍵字創建類,默認的可見性修飾符是 public。接下來創建一個對象:
val p = Person()
和 Java 不同的時,Kotlin 中創建對象時省略了new
關鍵字。
二、構造函數
Kotlin 中構造函數分為主構造函數和次構造函數,每個類默認都會有一個不帶參數的主構造函數,例如上邊的Person
類。主構造函數的特點是沒有函數體,直接定義在類名的后面即可,下邊定義Person2
類,并添加帶參的主構造函數:
class Person2(var name: String, var age: Int) {
}
由于主構造函數沒有函數體,如果需要在主構造函數中編寫代碼該怎么做? Kotlin 提供了一個用init
關鍵字作為前綴的初始化塊,可以在這里編寫要在主構造函數中完成的業務:
class Person2(var name: String, var age: Int) {
init {
println("main constructor init")
}
}
注意主構造函數的參數如果使用了var
或val
修飾符,就相當于在類中聲明了對應名稱的屬性。
一個類可以有多個次構造函數,用constructor
關鍵字聲明。Kotlin 中規定,當一個類既有主構造函數又有次構造函數時,所有次構造函數都必須使用this
關鍵字直接或間接的調用主構造函數(當類名后加了圓括號,無論有沒有參數,就需要遵守這個規定):
class Person2(var name: String, var age: Int) {
init {
println("main constructor init")
}
constructor(name: String, age: Int, sex: String) : this(name, age) {
}
constructor(name: String, age: Int, sex: String, idCard: String) : this(name, age, sex) {
}
}
第一個次構造函數直接調用了主構造函數,第二個次構造函數調用了第一個次構造函數,也就是間接的調用了主構造函數。
三、繼承
我們上邊聲明的Person
、Person2
是不能被直接繼承的,因為在 Kotlin 中非抽象類是不能被直接繼承的,考慮到抽象類需要有子類繼承它才能創建對象,而非抽象類卻不需要,所以 Kotlin 默認讓非抽象類不能被繼承,以防產生不可預知的問題。當然這個默認規則是可以改變的,只需要在非抽象類前加上open
關鍵字即可:
open class Person {
}
Kotlin 中用冒號:
實現繼承,而不是 Java 中的extends
關鍵字,先看一個簡單的:
class Student : Person() {
}
怎么要在被繼承的類后加一個括號()
?這點又和 Java 的類繼承不同。在 Java 的繼承特性中有一個規則,子類的構造函數需要調用父類的構造函數,在 Kotlin 中我們同樣需要遵循這個規則。這個()
其實就是調用父類的無參構造函數。當 Student 類的主構造函數初始化時會調用 Person 類的無參數構造函數。
當然,繼承一個類時也可以調用父類的有參構造函數:
class Student(name: String, age: Int) : Person2(name, age) {
}
這里繼承了 Person2 類,并調用了其兩個參數的主構造函數,由于 Person2 類的主構造函數需要兩個參數,我們需要給 Student 類定義了包含這兩個參數的主構造函數,并將參數傳遞給 Person2 類的構造函數。
如果子類沒有主構造函數,有次構造函數,同時父類的構造函數都是帶參的,那么繼承父類時,如何調用父類的帶參構造函數呢?雖然我們一般不會這樣定義類來為難自己,但這在 Kotlin 中是支持的。因為子類有次構造函數,所以通過次構造函數,使用super
關鍵字調用父類的帶參構造函數:
class Student : Person2 {
constructor(name: String, age: Int) : super(name, age) {
}
}
由于是通過次構造函數調用父類的帶參構造函數,所以 Person2 后的括號()
就省略了。