1. 程式人生 > >Kotlin-抽象類和介面

Kotlin-抽象類和介面

對於面向物件程式設計來說,抽象是它的一大特徵之一。在Kotlin中可以通過抽象類和介面來完成抽象。抽象類和介面有很多相似之處,又有不同之處。

抽象方法

抽象方法是一種特殊的方法:它只有宣告,而沒有具體的實現。抽象方法的宣告格式為:

abstract fun doSwim()
  1. 抽象方法必須用abstract關鍵字進行修飾
  2. 抽象方法不用手動新增open,預設被open修飾
  3. 抽象方法沒有具體的實現
  4. 含有抽象方法的類成為抽象類,必須由abtract關鍵字修飾

抽象屬性

抽象屬性就是在var或val前被abstract修飾,抽象屬性的宣告格式為:

abstract var addr : String
abstract val weight : Float

1. 抽象屬相在抽象類中不能被初始化
2. 在子類沒有主建構函式,要對抽象屬性,手動初始化。如果子類中有主建構函式,抽象屬性可以在主建構函式中宣告

class Student(name : String, age : Int, var no : String, var score : Int) : Person(name, age) {


    override var addr: String
        get() = ""
        set(value) {
        }
    override val weight: Float
        get() = 0F

    override fun doSwim() {

        println("doSwim")
    }
}

class Student(name : String, age : Int, var no : String, var score : Int, override var addr: String, override val weight: Float) : Person(name, age) {


    override fun doSwim() {

        println("doSwim")
    }
}
  1. 抽象屬性只能在抽象類中宣告
    4.

抽象類

含有抽象方法的類,稱為抽象類。在抽象類中,不僅可以有抽象方法,同時可以有具體實現的方法。

abstract class Person(var name : String, var age : Int) : Any() {

    abstract var addr : String
    abstract val weight : Float

    abstract fun doSwim()

    fun doFly() {
        println("doFly")
    }

    fun doEach() {
        println("doEach")
    }
}

抽象類和普通類的主要有三點區別:

  1. 抽象方法必須為public或者protected(因為如果為private,則不能被子類繼承,子類便無法實現該方法),預設情況下預設為public。
  2. 抽象類不能用來建立物件;
  3. 如果一個類繼承於一個抽象類,則子類必須實現父類的抽象方法。如果子類沒有實現父類的抽象方法,則必須將子類也定義為為abstract類。如果抽象類中含有抽象屬性,再實現子類中必須將抽象屬性初始化,除非子類也為抽象類。

介面

Kotlin的介面類似於java 8,它們都可以包含抽象方,以及方法的實現。

介面和抽象類不同的是,介面不能儲存狀態,可以有屬性但必須是抽象的。

介面是通過關鍵字 interface 來定義的:

interface MyInterface {

    val func : Int

    fun bar()
    fun foo() {
    //函式體是可選的
    }
}

接#口的實現

一個類中可以實現一個或多個介面。

class Child : MyInterface {
    fun bar () {
    //函式體
    }
}

介面中的屬性

因為介面沒有狀態, 所以中只允許有無狀態的屬性。

重寫衝突

當我們在父類中聲明瞭許多型別, 有可能出現一個方法的多種實現。 比如:
interdace A {
fun foo() { print(“A”) }
fun bar()
}

interface B {
    fun foo() { print("B") }
    fun bar() { print("bar") }
} 

class C : A {
    override fun bar() { print("bar") }
} 

class D : A, B {
    override fun foo() {
        super<A>.foo()
        super<B>.foo()
    }

    override fun bar() {
        super.bar()
    }
}

介面A、B都有聲明瞭foo()、bar()方法,它們都實現了 foo() 方法, 但只有介面B中實現了bar() ,在介面A中bar()方法被宣告為抽象方法,這是因為在介面中如果函式沒有函式體,那麼預設是抽像的。C類作為實體類,繼承於介面A,C類必須重寫bar(),並將其實現。D類作為實體類,繼承於介面A和B,由於bar()方法在介面B中已實現,此方法不需要重寫。如果想呼叫B介面的實現需使用super關鍵字呼叫,呼叫方式為super.bar()。但是對於foo()方法,兩個介面都有實現,此時編輯器不知該如何抉擇,此時會強制我們重寫foo(),並要求明確該方法的呼叫及實現。

抽象類和介面的差異

語法層面上的區別

  1. 介面不能儲存狀態,可以有屬性但必須是抽象的,而抽型別可以有屬性。
  2. 一個類只能繼承一個抽象類,而一個類卻可以實現多個介面。

設計層面上的區別

  1. 抽象類是對一種事物的抽象,即對類抽象,而介面是對行為的抽象。抽象類是對整個類整體進行抽象,包括屬性、行為,但是介面卻是對類區域性(行為)進行抽象。舉個簡單的例子,飛機和鳥是不同類的事物,但是它們都有一個共性,就是都會飛。那麼在設計的時候,可以將飛機設計為一個類Airplane,將鳥設計為一個類Bird,但是不能將 飛行 這個特性也設計為類,因此它只是一個行為特性,並不是對一類事物的抽象描述。此時可以將 飛行 設計為一個介面Fly,包含方法fly( ),然後Airplane和Bird分別根據自己的需要實現Fly這個介面。然後至於有不同種類的飛機,比如戰鬥機、民用飛機等直接繼承Airplane即可,對於鳥也是類似的,不同種類的鳥直接繼承Bird類即可。從這裡可以看出,繼承是一個 “是不是”的關係,而 介面 實現則是 “有沒有”的關係。如果一個類繼承了某個抽象類,則子類必定是抽象類的種類,而介面實現則是有沒有、具備不具備的關係,比如鳥是否能飛(或者是否具備飛行這個特點),能飛行則可以實現這個介面,不能飛行就不實現這個介面。

  2. 設計層面不同,抽象類作為很多子類的父類,它是一種模板式設計。而介面是一種行為規範,它是一種輻射式設計。什麼是模板式設計?最簡單例子,大家都用過ppt裡面的模板,如果用模板A設計了ppt B和ppt C,ppt B和ppt C公共的部分就是模板A了,如果它們的公共部分需要改動,則只需要改動模板A就可以了,不需要重新對ppt B和ppt C進行改動。而輻射式設計,比如某個電梯都裝了某種報警器,一旦要更新報警器,就必須全部更新。也就是說對於抽象類,如果需要新增新的方法,可以直接在抽象類中新增具體的實現,子類可以不進行變更;而對於介面則不行,如果介面進行了變更,則所有實現這個介面的類都必須進行相應的改動。