轉向Kotlin——列舉類和擴充套件

Android機動車發表於2018-04-25

列舉類

Kotlin中的列舉類和Java中的列舉型別非常相似,具有類的特性。一般將可列舉的同型別的一組值作為列舉類定義。

基本用法

Kotlin中,列舉型別以類的形式存在,因此叫做列舉類,如下是例項:

enum class Color {
    RED, GREEN, BLUE
}
複製程式碼

Kotlin中一切都是物件,因此,每一個列舉型別也是物件,多個列舉型別之間用 , 隔開。

先看下如何使用:

var color: Color = Color.BLUE

var color2 = Color.GREEN

// 比較兩個列舉型別變數
var bool: Boolean = color == color2
複製程式碼

需要注意,引用列舉類中的值,需要加上列舉類名。預設狀態下,直接輸出列舉類的元素值,會輸出元素值的名稱。

為列舉值指定數值

其實列舉類每一個值就是當前列舉類的物件,因此,如果要為每一個列舉類的值指定一個數字,直接通過構造器傳入即可。

enum class Direction private constructor(var value: Int) {
    NORTH(1), WEST(2), EAST(3), SOUTH(4)
}
複製程式碼

其他功能

首先說明一點,為每一個列舉值指定一個值,這個數並不一定從0開始,也不一定是按順序的,因此列舉值在列舉類中的位置和列舉值對應的數值可能並不相同。

無論是Java還是Kotlin都提供了相應的API來獲取列舉值的名字和索引。Kotlin提供了name和ordinal屬性,分別用於獲取列舉值名和索引。

println(color.name) // 返回列舉的名字
println(color.ordinal) // 返回列舉的索引
複製程式碼

除此以外,還可以使用valueOf方法傳入列舉值名稱來獲取列舉值對應的數值。

擴充套件

擴充套件是Kotlin中非常重要的功能,通過擴充套件,可以在沒有原始碼的情況下向類中新增成員。也可以在團隊開發的情況下,通過擴充套件,將功能模組分散給多個人開發。

擴充套件原生API

儘管JDK和Kotlin原生提供了很豐富的API,但這些API似乎仍然不夠用,就需要為這些Library新增額外的API。

在類的外部對系統的類進行擴充套件,由於將擴充套件的部分都放到了自己的原始碼中,因此這些原始碼仍然可以執行在其他機器的JDK和Kotlin執行時上。Kotlin既可以對JDK的API進行擴充套件,也可以對Kotlin進行擴充套件。

fun ArrayList<Int>.swap(index1: Int, index2: Int) {
    val tmp = this[index1]
    this[index1] = this[index2]
    this[index2] = tmp
}
複製程式碼

上面的程式碼是對原生集合類ArrayList進行擴充套件,擴充套件了swap交換方法。

這段程式碼放到哪個Kotlin檔案中都可以,一般會放到Kotlin檔案頂層,當然,也可以放在呼叫swap方法的前面。

擴充套件自定義類

擴充套件類的目的右很多,除了系統類需要擴充套件外,我們自己編寫的類也需要擴充套件,擴充套件自定義類的方法和擴充套件系統類相同:

open class Parent(var value1: Int, var value2: Int) {
    var mVaule1 = value1
    var mValue2 = value2
    fun add(): Int {
        return mVaule1 + mValue2
    }
}

class Child(value1: Int, value2: Int) : Parent(value1, value2) {
    fun sub(): Int {
        return value1 - value2
    }
}

// 通過擴充套件向Parent類新增一個輸出結果方法
fun Parent.printlnResult() {
    println("${value1}+${value2}=${add()}")
}

fun Child.printlnResult() {
    println("${value1}-${value2}=${sub()}")
}
複製程式碼

因為open布恩那個用在頂層函式中,所以通過擴充套件是不能新增可繼承的成員函式的(Kotlin預設不允許繼承)。

成員函式衝突的解決

如果通過擴充套件向類中新增的成員函式與類中原來的成員函式的結構完全相同,那麼哪個優先呢?

答案是:內部成員函式的優先順序更高,因此,通過擴充套件方法無法覆蓋內部成員函式。

擴充套件屬性

擴充套件屬性和擴充套件方法類似,Kotlin屬性在類中必須初始化,而初始化需要使用backing field,也就是那個field欄位,可以將屬性設定的值儲存在field中,也可以從field獲得屬性值。但是通過擴充套件的屬性是沒有backing field的,因此,擴充套件屬性需要實現setter部分,這樣才能為擴充套件屬性初始化。

class MClass {
    var mValue: Int = 0
    var str: String = ""
        get() = str
        set(value) {
            field = value
        }
}

// 屬性擴充套件
var MClass.newVaule: Int
    get() = mValue
    set(value) {
        mValue = value
    }
複製程式碼

由於擴充套件屬性屬性沒有backing field欄位,因此儲存和獲取屬性值,需要使用一個類成員變數。但成員變數需要宣告為public,否則擴充套件屬性無法訪問。

擴充套件伴隨物件

伴隨物件:解決Kotlin類中沒有靜態成員所帶來的尷尬。

如果類中右伴隨物件,可以利用擴充套件為伴隨物件新增成員。

class MClas {
    companion object {

    }
}

fun MClas.Companion.func() {
    println("伴隨物件成員函式")
}
複製程式碼

擴充套件的範圍

以上編寫的擴充套件程式碼都是在同一個包的同意個kotlin檔案中,當然,同一個包的不同Kotlin檔案中也是一樣的,但是如果在不同包中的Kotlin裡,就要使用import匯入相應的資源了。之前介紹過import導包,就不再累贅了。

類中的擴充套件

其實,擴充套件也可以在類中定義。

class D {
    fun bar() {
        println("D.bar")
    }
}

class C {
    fun baz() {
        println("C.baz")
    }

    // 在類中對另一個類進行方法擴充套件
    fun D.foo() {
        bar()
        baz()
    }

    fun caller(d: D) {
        d.foo()
    }
}
複製程式碼

呼叫特定類的成員函式

如果在B中擴充套件A,那麼在A的擴充套件方法中呼叫A和B都有的成員函式,到底是呼叫A的還是B的呢?如下:

class A {
    fun func() {
        println("A.func")
    }
}

class B {
    fun func() {
        println("B.func")
    }

    fun A.doo() {
        func() // 呼叫A類的func方法
        this@B.func() // 呼叫B類的func方法
    }

    fun call(a: A) {
        a.doo()
    }
}
複製程式碼

如果在A.doo中呼叫B的func函式,需要使用this@B.func(),而呼叫A類中的方法可以直接使用。

擴充套件成員的繼承

以前提到過,擴充套件成員是不能被繼承的,其實這個說法是不準確的。更準確的說法是:解除安裝頂層的擴充套件成員不能被繼承,因為無法新增open關鍵字修飾。但在類中對另一個類擴充套件卻可以新增open關鍵字。

小結

儘管列舉類並不是在程式碼中經常出現,但用來定義可列舉的一組相關值還是非常好的,至少讓程式碼變得更可讀(遠比使用常量或直接使用數字要好)。而擴充套件在很多語言中都支援,充分利用Kotlin擴充套件,可以讓程式碼變得更容易維護,同時也帶來了更大的靈活性。

更多精彩內容,歡迎關注我的微信公眾號——Android機動車

這裡寫圖片描述

相關文章