天天看點

Kotlin學習之泛型的進階特性

泛型實體化

Java中的泛型是通過類型擦除機制來實作的,而Kotlin卻允許将内聯函數中的泛型進行實化。

要将某個泛型實化需要兩個前提條件。首先,該函數必須是内聯函數才行,也就是要用inline關鍵字來修飾該函數。其次,在聲明泛型的地方必須加上reified關鍵字來表示該泛型要進行實化。示例代碼如下:

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

然後我們就可以通過以下代碼獲得泛型的具體類型了:

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

運作結果如下

Kotlin學習之泛型的進階特性

泛型實體化的應用

泛型實體化的功能允許我們在泛型函數當中獲得泛型的實際類型,也就是的 a is T, T::class.java 這樣的文法成為了可能。

對于啟動一個Activity,以前我們使這樣寫的

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

但是Kotlin的泛型實體化讓我們有了更好的選擇

建立一個reified.kt

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

本來Intent接收的第二個參數應該是一個具體的Activity的Class類型,但由于T是一個被實體化的泛型,是以這裡我們直接傳入T::class.java。

如果我們想要啟動TestActivity,隻需要這樣寫就可以了

同時,可能intent會附帶一些參數,如下寫法

val intent = Intent(context, TestActivity::class.java)
intent.putExtra("param1","data")
intent.putExtra("param2",123)
context.startActivity(intent)
           

而通過上面的封裝,我們就沒法進行傳參了,但是我們可以通過高階函數類解決

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

通過添加一個函數類型參數,這樣就可以在調用startActivity()函數時使用Lambda表達式為Intent傳參

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

協變和逆變

在開始學習協變和逆變之前,我們還得先了解一個約定。一個泛型類或者泛型接口中的方法,它的參數清單是接收資料的地方,是以可以稱它為in位置,而它的傳回值是輸出資料的地方,是以可以稱它為out位置,如下圖所示。

Kotlin學習之泛型的進階特性

協變的定義:假如定義了一個MyClass的泛型類,其中A是B的子類型,同時MyClass又是MyClass

的子類型,那麼我們就可以稱MyClass在T這個泛型上是協變的。

逆變的定義:假如定義了一個MyClass的泛型類,其中A是B的子類型,同時MyClass

又是MyClass的子類型,那麼我們就可以稱MyClass在T這個泛型上是逆變的。
Kotlin學習之泛型的進階特性

觀察如下代碼,我們在泛型T的聲明前面加上了一個out關鍵字。這就意味着現在T隻能出現在out位置上,而不能出現在in位置上,同時也意味着SimpleData在泛型T上是協變的。

class SimpleData<out T>(val data: T?) {
    fun get(): T? {
        return data
    }
}
           

觀察如下代碼,我們在泛型T的聲明前面加上了一個in關鍵字。這就意味着現在T隻能出現在in位置上,而不能出現在out位置上,同時也意味着Transformer在泛型T上是逆變的。

interface Transformer<in T> {
    fun transform(t: T): String
}
           

在out位置上,同時也意味着Transformer在泛型T上是逆變的。

interface Transformer<in T> {
    fun transform(t: T): String
}