1. 程式人生 > >【Kotlin從入門到深坑】之類的覆蓋屬性和方法以及抽象類

【Kotlin從入門到深坑】之類的覆蓋屬性和方法以及抽象類

簡介

本篇部落格主要是介紹Kotlin語法中的【類的覆蓋屬性和方法以及抽象類】相關的知識,幫助各位更快的掌握Kotlin,如果有疏漏或者錯誤,請在留言中指正,謝謝。 系列彙總地址

上一篇部落格中我們詳細介紹了類的繼承和構造,下面我們來講一下,在繼承時如何覆蓋方法。

覆蓋方法

我們先看一下如何去寫,例子如下:

// 父類,使用open關鍵詞
open class TestB {
   open fun test() { //需要被重寫的方法需要使用open修飾

    }
}
//子類
class TestA() : TestB() {
    override fun test() {//重寫的方法需要使用override
super.test() } }

方法和類一樣,預設是final的禁止重寫的,所以必須加上open修飾,而重寫的方法也必須加上override,否則子類不允許相同簽名的函式。

還有一點是需要注意的,預設override的函式是open的,如果你想再次限制它被覆蓋,可以使用final修飾,程式碼如下:

//子類
class TestA() : TestB() {
    final override fun test() {//重寫的方法需要使用override,使用了final,後續無法覆蓋
        super.test()
    }
}

覆蓋屬性

我們先看一下如何去寫,例子如下:

//父類
open class TestB {

   open var x: Int? = null //屬性x
        get() {//重寫了get方法,此處後面詳解
            field = 34
            return field
        }
}

class TestA() : TestB() {
    override var x: Int?=null //它覆蓋對應屬性後,對應的方法也被覆蓋


}

對於Kotlin中的屬性都有預設的get、set方法,當然也可以重寫,比如上面的TestB的x的屬性的get

方法被重寫了。但是TestA 覆蓋屬性後,TestB中定義的get方法對其無效,舉例如下,便於理解其含義:

 var a = TestA()
    println(a.x)

    var b=TestB()
    println(b.x)

輸出結果:

null  //說明TestB的get方法對TestA類無效
34   //說明TestB的get方法對TestB類有效

注意:如果原型別為var,則覆蓋型別可以為var,且不可以是val,如果原型別是val,覆蓋型別可以是val,也可以是var

多實現

在Kotlin中,對於繼承來說,如果一個類從它的直接超類繼承相同成員的多個實現,它必須覆蓋這個成員並提供自己的實現。為了表達採用從哪個超型別繼承實現,我們使用super<父類名>,下面舉例說明:

//首先宣告一個類
open class TestB {

   open var x: Int? = null
        get() {
            field = 34
            return field
        }

    open fun test() { //此處有一個可以覆蓋的方法test()

    }


}
//宣告一個介面
interface TestC { //這個是介面的宣告方式
    var x: Int? //聲明瞭一個抽象欄位,實現類必須覆蓋此屬性
    fun test() { //聲明瞭一個方法,這個是特殊的,可以有實現方法
        print("d")
    }

    fun test2() //這個是和java一致的抽象方法
}

此處需要注意此處的介面是可以實現具體的方法的

//實現類TestA
class TestA() : TestC, TestB() {

    override fun test2() {//對於一般的抽象方法等同於java中,必須實現,否則宣告成抽象類

    }

    override fun test() {//此處必須寫,因為test有兩種不同實現
        super<TestC>.test()
        super<TestB>.test()
        //上面兩個都可不寫,也可以都寫,類似於super.method

    }

    override var x: Int? = null //對於欄位沒影響,因為介面的屬性不允許賦值,


}

我們總結一波:

  • 首先繼承TestBTestC是正常的
  • 由於TestBTestC內都有test方法,所以TestC必須實現test方法
  • 在子類TestC中使用super<TestB>.test()或者super<TestC>.test() 來呼叫父類的實現

上面我們說的是方法,仔細的話,我們還能看到介面還有屬性的定義,對於TestBTestC都有x欄位,TestA中進行重寫的話,get/set都會被覆蓋,所以不會出現上面方法的問題

還需要注意,看一下程式碼

open class TestB {//類的一個成員變數
    open var x: Int? = null
}

interface TestC { //這個是介面的宣告方式
    var x: Int? //聲明瞭一個抽象欄位,實現類必須覆蓋此屬性

}

//實現類
class TestA() : TestC, TestB() {
}

我們總結一波:

  • 對於介面來說,他的方法預設是抽象的,除非自己實現了
  • 對於介面的成員變數也是一樣的,也是預設抽象的,子類必須覆蓋
  • 但凡事都有例外,對於上面的實現中,我們並沒覆蓋x,但仍然正確是,因為,介面只是約束作用,因為TestA繼承了TestB,也就有了X,欄位,而介面也就是約束必須有該欄位即可,所以,上面的實現也是允許的

抽象類

類和類中的某些成員可以宣告成abstract ,抽象成員在本類中可以不去實現,需要注意的是我們不需要用open標註抽象類或者函式,更有趣的是看如下實現:

//開放的類和方法
open class A{
    open fun  test(){

    }
}
//抽象類
abstract class B : A() {
    abstract override fun test() //抽象方法覆蓋開放方法
}

我們可以用一個抽象成員去覆蓋一個非抽象的開放成員

伴生物件

與java不同,Kotlin沒有靜態方法,官方建議使用包級函式,可以使用如下兩種方式來實現類似靜態類、靜態方法的方式
第一種:

object StaticDemo{
    fun test(){
        print("")
    }
}

使用方法如下:

  StaticDemo.test()

第二種:

class Demo {
    companion object {//宣告在內部的,類似靜態方法,可以使用 類名.方法名
        fun test() {
            print("")
        }
    }

    fun test2() { //test2在外部,必須例項化後才可呼叫

    }

}

使用方法:

  Demo.test()

下面我們總結一波:

  • 無論方法還是類除了抽象類、抽象方法、介面、介面方法外,其他的繼承都需要寫open,重寫的成員變數和方法需要override

  • 與java不同,Kotlin中的介面可以自己實現方法,從而導致了出現多實現的問題,出現多實現的,繼承的子類需要重寫對應的方法,然後決定是否或者呼叫哪個父類的方法。

總結

至此已經學完了Kotlin的【類的覆蓋屬性和方法以及抽象類】相關的知識,多回顧多思考,繼續後續內容