kotlin學習筆記——泛型及reified函數

Kotlin學習筆記系列:http://blog.csdn.net/column/details/16696.html


kotlin中的泛型與java中的差不多
class TypedClass<T>(param : T){
     ...
}

val s = TypedClass<String>("hello")

kotlin中可以簡化模板代碼,編譯器可以自動推斷類型,如
val s = TypedClass("hello")

但如果接收一個null,編譯器無法推斷類型,仍然需要指定類型,如
val s = TypedClass<String?>(null)

我們可以給泛型添加限制,如:
class TypeClass<T : Any>(param : T){
     ...
}
這時限制爲非null類型,上面接收null的代碼就會報錯了。

我們也可以限制爲某種類型的子類,與java中類似,如
class TypedClass<T : Context>(param : T){
     ....
}
這樣所有繼承至Context的類可以使用,其他則不能。

泛型也可以使用在函數中,如
fun <T> do(param : T) List<T>{
     ...
}




泛型的變體
在java中,使用泛型時會遇到一些問題,比如使用集合的時候,如
List<String> list1 = Lists.newArrayList();
List<Object> list2  = list1
就會報錯,雖然List<Object>有更弱的限制,因爲如果編譯器允許這種行爲,後面如果出現下面的代碼就一定會程序崩潰。
list2.add(5);
String s = list2.get(0);
但是在java中,我們可以使用addAll,如
List<Object> list2  = Lists.newArrayList();
list2.addAll(list1)
這時因爲addAll的函數接收的參數是“Collection<? extends E> items”,這裏就用到了泛型的協變。
爲了解決類似的問題,爲泛型增加了通配符,包括協變和逆變
協變(covariance)—— “? extends XXX” —— 表示限制爲XXX及其子類
逆變(contravatiance)—— “? super XXX” —— 表示限制爲XXX及其父類
這樣就可以處理上面List的問題,如
List<? extends Object> list2 = list1;
但是通配符都有它的限制,通配符定義的了使用場景變體(use-site variance),當我們使用它的時候都需要聲明它。這表示我們每次聲明一個泛型變量都需要增加模板代碼。例如上面的代碼就增加了一下額外的模板代碼。

在kotlin中,我們可以使用內部聲明變體(declaration-site variance)這種更簡單的方式,來表示當我們定義一個類或接口時可以處理弱限制場景。
相比冗長的通配符,在kotlin中我們可以使用“out”來針對協變,使用“in”來針對逆變。如
class TypedClass<out T>{
     ...
}

val obj1 = TypedClass<String>()
val obj2 : TypedClass<Any> = obj1
這段代碼可以在kotlin中編譯運行(最後一行實際上就相當於val obj2 : TypedClass<? extends Any> = obj1)




下面我們再來說說reified函數,它與泛型密切相關
比如我們啓動一個activity代碼:
val intent = Intent(MainActivity@this, javaClass<DetailActivity>)
intent.putExtra(DetailActivity.ID, item.id)
intent.putExtra(DetailActivity.NAME, item.name)
startActivity(intent)

anko給我們提供了一個reified function,可以更簡單的實現
startActivity<DetailActivity>(DetailActivity.ID to item.id, DetailActivity.NAME to item.name)

我們知道在java中使用泛型的時候,無法通過泛型來得到Class,一般我們會將Class通過參數傳過去。
在kotlin中一個內聯函數(inline)可以被具體化(reified),這意味着我們可以得到使用泛型類型的Class。
startActivity的源碼如下:
public inline fun <reified T: Activity> Context.startActivity(
        vararg params: Pair<StringString>) {
    val intent = Intent(this, T::class.javaClass)
    params forEach { intent.putExtra(it.first, it.second) }
    startActivity(intent)
}
可以看到使用T::class.javaClass就可以得到使用泛型類型的Class。
startActivity是Context的內聯函數,所以要在Context類中纔可以使用,在其他地方使用則需要使用with函數:
with(context){
     startActivity<...>(...)
}





發佈了106 篇原創文章 · 獲贊 30 · 訪問量 26萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章