1. 程式人生 > 實用技巧 >Kotlin進階學習4

Kotlin進階學習4

寫在前面

本文接上文:Kotlin進階學習3。上次文章主要學習了泛型的一些基本用法,這次來學習一下泛型的進階用法。這部分還是有很大的難度的,勉強記錄一下。

泛型的實化

介紹

泛型實化這個概念,其實對於學Java的很陌生。這裡得先解釋一下Java的泛型擦除機制。Java的泛型功能是通過型別擦除機制來實現的。就是說泛型對於型別的約束只在編譯時期存在,執行的時候仍然按照JDK1.5之前的機制(即無泛型),JVM是識別不出來我們在程式碼中指定的泛型型別的。也就是說比如我們建立了一個List,雖然我們在編譯約束我們必須輸入String型別,但在執行時期只能識別出來他是個List,並不知道他的包含哪種型別。

所有基於JVM的語言,泛型功能都是基於型別擦除實現的。其中自然包括了Kotlin,因此我們無法使用T::class.java這樣的語法,因為T的實際型別在執行的時候已經沒了。

但Kotlin有一個行內函數的概念,這也就意味著Kotlin中是可以將行內函數中的泛型進行實化的。

使用

要使用泛型實化,首先必須是行內函數,其次要在宣告泛型的地方加上reified關鍵字來表示該泛型要進行實化:

inline fun <reified T> getGenericType(){}

既然泛型實化了,那麼就可以實現Java中不可能實現的一些功能了:

inline fun <reified T> getGenericType() = T::class.java

測試程式碼:

fun main(){
    val result1 = getGenericType<String>()
    println("result1 is $result1")
}

可以看到,我們實現了一個不可思議的功能。可以獲得型別了。

應用

我們可以使用泛型實化對安卓中的一些操作進行優化,比如啟動activity的操作:

val intent = Intent(context,TestActivity::class.java)
context.startActivity(intent)

我們新建一個kt檔案:

inline fun <reified T> startActivity(context:Context){
    val intent = Intent(context,T::class.java)
    context.startActivity(intent)
}

這裡我們定義了一個方法,裡面實現了Activity的啟動。這樣我們要寫啟動activity時,就十分簡單了:

startActivity<TestActivity>(context)

但這樣似乎不能實現intent附加引數的用法了。我們可以改一下程式碼:

inline fun <reified T> startActivity(context:Context,block:Intent.() -> Unit){
    val intent = Intent(context,T::class.java)
    intent.block()
    context.startActivity(intent)
}

這裡我們使用了高階函式,定義了一個在Intent類裡的拓展方法,這樣就可以實現傳入值的做法了:

startActivity<TestActivity>(context){
    putExtra("param1","data")
    putExtra("param2",123)
}

這樣看來,這種寫法要比之前的寫法要看起來舒服的多。

泛型的協變和逆變

這方面的內容我看了好幾遍書,愣是沒看懂。先佔個坑,等看懂了再來繼續寫。當然也可能不更新了,畢竟這兩個特性並不是很常用。

委託

介紹

委託是一種設計模式。它的基本理念是操作物件自己不會去處理某段邏輯,而是委託給另外一個輔助物件去處理。Kotlin中也是支援支援委託功能的,分為類委託和委託屬性。我們分條學習:

類委託

類委託的核心思想在於將一個類的具體實現委託給另外一個類去完成。我們都知道Set這種資料結構,它是一個介面,使用時要呼叫他的實現類,比如HashSet。而藉助委託模式我們可以輕鬆實現一個自己的實現類:

class MySet<T>(val helperSet:HashSet<T>): Set<t> by helperSet{}

其中by是Kotlin中委託使用的關鍵字。這樣子聲明後,當我們需要對某個方法重新實現的時候,只需要單獨重寫那一個方法就可以了。類委託可以讓我們免去重寫那些大量重複的模版程式碼,而只專注於自己的方法或者重寫需要重寫的方法。

委託屬性

委託屬性的核心思想在於將一個屬性(欄位)的具體實現委託給另一個類去完成,比如:

class MyClass{
    var p by Delegate()
}

這樣的寫法,意味著將p屬性的具體實現交給了Delegate類去完成,當呼叫p屬性的時候自動呼叫Delegate類的getValue()方法,當賦值的時候自動呼叫Delegate類的setValue()方法。因此,我們也得實現Delegate類才行:

class Delegate{
    var propValue:Any? = null
    operator fun getValue(myClass:MyClass,prop:KProperty<*>):Any?{
        return propValue
    }
    operator fun setValue(myClass:MyClass,prop:KProperty<*>,value:Any?){
        propValue = value
    }
}

這是一種標準的程式碼實現模版。在Delegate類中必須實現getValue()和setValue()方法,且必須都要使用operator關鍵字進行宣告。

在方法上,出現了一個KProperty<*>,它是Kotlin中的一個屬性操作類,可用於獲取各種屬性相關的值。雖然在這用不著,但必須加上宣告。<*****>代表你不知道或不關心泛型的具體型別,只是為了通過語法編譯而已。

實際上,有一種情況也可以不用在Delegate類中實現setValue()方法,那就是MyClass中的p屬性是使用val關鍵字宣告的。也很好理解,val關鍵字是無法在初始化後被賦值的,自然也就不需要實現setValue()方法了。

總結

本次學習的內容有點少,因為Kotlin的知識學習的也差不多了。接下來會嘗試記錄下協程的相關內容,協程作為Kotlin中很有特色的一個功能,內容很多。就不在這裡展開了。總的來說,這裡學習了Kotlin中的泛型的一些高階特性,補充了委託的相關內容。實際上這都是一些簡單的筆記,具體的使用還是要在實際開發中應用。