簡介
本篇博客主要是介紹Kotlin語法中的【類和繼承】相關的知識,幫助各位更快的掌握Kotlin,如果有疏漏或者錯誤,請在留言中指正,謝謝。 系列彙總地址
類
聲明類
Kotlin中使用關鍵詞class 聲明類和java中保持一致,代碼如下:
class Demo{ //class + 類名
}
類的構成=class +類名+類頭(可選)+類體(可選),如果沒有類體,括號也可省略
class Demo //class + 類名
主構造函數
在Kotlin的類中可以有一個主構造函數和多個次構造函數。我們看看什麼樣的算主構造函數,例子如下:
class Demo(){
}
看了例子估計心裏開始有疑惑了,這和上面的例子有什麼不同??仔細一看還真有不同,多了”()”,這就聲明瞭一個無參的主構造函數,當然你也可以聲明有參數的,如下
class Demo(name: String){
}
主構造函數只是類頭的一部分,還有一個可選關鍵詞–constructor,當主構造函數想聲明爲非public 類型的時候,需要使用該關鍵詞其他時候可以省略,例如:
class Demo private constructor(name: String){
}
這種情況一般是在寫單例的時候會用到。
構造函數都有了,我們可以愉快的做些初始化了,但是但是要注意,主構造函數內不能包含任何代碼。 這個時候我們需要使用另一個關鍵詞 init
,使用init
代碼塊進行初始化代碼即可,如下:
class Demo private constructor(name: String) {
init {//init代碼塊
println("測試輸出")
}
}
上面的代碼中name
可以直接使用在 init
代碼塊內和類體內聲明的屬性初始化中使用,只有這兩種情況,代碼如下
class Demo (name: String) {
init {
println("測試輸出"+name) //在此處使用可以
}
var nameTest=name //此處給nameTest賦值可以,但不可以單獨使用
}
當然我們可以讓上面的name
有更多的用途怎麼辦呢?可以這樣寫:
class Demo(var name: String) { //此處加入 var關鍵詞
init {
println("測試輸出" + name)
}
var nameTest = name
fun printTest() { //方法內也可使用了
println(name)
}
}
不僅可以使用var
關鍵詞,還可以使用val,具體的差別之前我們說過,再次不再贅述。
使用該關鍵詞後,name就等同於是該類的成員變量,就等同於你直接聲明在類中。
對於Kotlin來說還有個好用的地方,就是你聲明的類中變量不需要寫get/set方法,默認就會有,可以直接用。
次構造函數
Kotlin中類也可以聲明前綴有 constructor 的次構造函數,如下:
class Demo { //有個默認的無參主構造函數
constructor(){// 有個無參次構造函數
}
}
如果類有一個主構造函數,每個次構造函數需要委託給主構造函數, 可以直接委託或者通過別的次構造函數間接委託。委託到同一個類的另一個構造函數 用 this 關鍵字即可。
下面根據主構造函數是否重寫來分別講解,先說未重寫:
class A{
}
如何寫它的次構造函數呢?
class A {
init {
println("--A主構造--")
}
constructor(name: String) { //此處屏蔽了 默認的主構造函數
println("--A次構造--")
}
}
上面的寫法會導致Kotlin的默認無參主構造函數不可調用, 也就導致constructor(name: String):this()
失效,但我們上面說過必須要委託給主構造函數,這怎麼辦?其實Kotlin內部仍然會走默認的主構造函數,也就是說如果是默認主構造是可以省略不寫的,有的時候是想寫也沒法寫。
得到上面的結論是源於一個測試,代碼如下:
var a = A("name"); //此處已經無法調用無參的,也就是默認的主構造失效,內部使用this() 也是不可以的
後面的打印結果
--A主構造-- //仍然走了主構造函數,此時只有默認主構造函數
--A次構造-- //然後再走次構造
所以得到上面的結論,在此佐證。 後續用實際代碼詳解,此處挖坑。
有的筒子可能就會想了,如果我們仍然想要一個無參數的構造函數怎麼寫?
class A {
init {
println("--A主構造--")
}
constructor(name: String) {
println("--A次構造--")
}
constructor(){ //增加一個無參的次構造函數
}
}
還有另一個方式,就是重寫主構造函數,這兩種方法都類似 java中自己重寫一個無參的構造函數,現在就是區分了主、次構造函數的重寫。還需強調的是主構造如果重寫了,次構造不允許再重寫。下面寫個重寫主構造函數的,後面細說:
class A() { //重寫此處
init {
println("--A主構造--")
}
constructor(name: String) : this() {
println("--A次構造--")
}
// constructor(){ //次構造不允許使用
//
// }
}
下面我們講一下重寫主構造函數的
如下:
class Demo() { //沒參數的主構造函數
constructor(name: String) : this() { //次構造函數委託給
}
}
此處就和默認的不一樣了,這個地方需要使用this(),因爲你重寫了主構造函數。如果不重寫是可以不寫this()
通過上方的各種對比和例子我們對類的構造函數有了大概的瞭解,我們爲了方便理解在這裏總結一波,還有不明白的多看看例子:
類只有一個主構造函數,但可以有一個或多個次構造函數
次構造函數需要委託給主構造函數,可以通過直接或者間接的方式
在聲明期間,以主構造函數爲主,如果主構造函數重寫了對應的構造方法,次構造函數不能用重複的出現對應的構造函數,上面有例子說明。
在調用期間,以次構造函數爲主,如果未重寫主構造函數,默認會有一個無參的主構造函數,如果重寫一個無參次構造函數,則可調用的只有次構造函數。
創建類的實例
要創建一個類的實例,我們就像普通函數一樣調用構造函數:
val invoice = Invoice()
val customer = Customer("Joe Smith")
//注意 Kotlin 並沒有 new 關鍵字。
繼承
在 Kotlin 中所有類都有一個共同的超類 Any,這對於沒有超類型聲明的類是默認超類:
class Example // 從 Any 隱式繼承
Any 不是 java.lang.Object;尤其是,它除了 equals()、hashCode()和toString()外沒有任何成員。 更多細節請查閱Java互操作性部分。
要聲明一個顯式的超類型,我們把類型放到類頭的冒號之後:
open class A(p: Int) //注意此處的 open 關鍵詞
class B(p: Int) : Base(p) //使用" :"
對於繼承的類來說,它仍需滿足之前類中主次構造函數的規定,還需滿足繼承類委託給被繼承類的主構造函數(保證對應類的init模塊都能運行的關鍵),此處的被委託的主構造函數如果是無參構造函數,可以省略不寫
下面我們分兩種類型去分別講解,首先是未修改默認的主構造函數的情況:
我們先看一下被繼承類:
open class BB { //未重寫默主構造
constructor(name: String) {
println(name + "---BB")
}
constructor() {
println(".....---BB")
}
}
繼承類:
class testA : BB { //此處testA也沒重寫主構造
constructor(name: String) : super(name) {//-- 1)
println("A---" + name)
}
constructor() {//-- 2)
println("A---" + "ss")
}
}
位置1處的構造函數,因爲未重寫主構造,所以本類的委託完成,所以只需委託父類即可,即可滿足上面說的要求。
位置2處的構造函數,因爲未重寫主構造,所以本類的委託完成,又因爲默認是委託父類的無參主構造函數(系統默認的或者重寫的都滿足),所以滿足上面說的要求。
我們做下改動,將B 改動如下:
open class BB() {//重寫默認主構造
constructor(name: String):this() { //手動委託主構造
println(name + "---BB")
}
// constructor() { //根據主次函數關係,此處無法存在
// println(".....---BB")
//
// }
}
這時候我們的繼承類如何寫呢?
class testA : BB {
constructor(name: String) : super(name) {
println("A---" + name)
}
constructor() {
println("A---" + "ss")
}
}
可以看到代碼是沒變化的,也就是說,只要是被繼承類的主構造函數,是無參的,無論是默認還是重寫都可以省略不寫,當然也可以寫上
class testA : BB {
constructor(name: String) : super(name) {//可以選擇不同的父類的主構造函數進行委託
println("A---" + name)
}
constructor():super() {//無參的委託
println("A---" + "ss")
}
}
下面我們將一下修改默認主構造函數的情況
class testB(name: String) : BB(name) { //此處testB重寫了默認主構造,所以此處B一定需要用主構造函數(直接或間接的)
init {
println(name + "99ss...---B")
}
constructor(name: String, age: Int) : this(name) {
println(name + "...---B")
}
}
也可以這樣寫:
class testB(name: String) : BB() { //此處必須有"()"
init {
println(name + "99ss...---B")
}
constructor(name: String, age: Int) : this(name) { //此處不能使用super了,不能重複委託父類,主構造函數已經委託過了
println(name + "...---B")
}
}
下面我們總結一波:
如果繼承的類重寫了默認主構造函數,此時必須用基類的主構造函數初始化(直接或間接)
如果繼承的類沒重寫默認的主構造函數,此時可以使用super關鍵詞初始化,我們也說過,未重寫的的默認主構造函數,次構造函數都會委託給它,所以本類內也滿足了委託條件,對於類外,也委託了被繼承類的主構造方法,完成了兩個類的條件
總結
至此已經學完了Kotlin的類和繼承相關的知識,多回顧多思考,繼續後續內容。