泛型的基本使用
泛型最常用于類和接口的定義中。例如:
class Box<T>(t: T) {
? ? var value = t
}
val box: Box<Int> = Box(1)
在這個例子中,Box類有一個泛型參數(shù)T传货,這意味著你可以用任何類型來實例化Box伴找,上述代碼中使用的是Int類型蜂厅。
泛型函數(shù)和屬性
你也可以在函數(shù)中使用泛型:
fun <T> singletonList(item: T): List<T> {
? ? return listOf(item)
}
val intList = singletonList(1) // 推斷出 List<Int>
在這里查库,singletonList 是一個泛型函數(shù)鼎姊,它接收一個類型為 T 的參數(shù)屠缭,并返回一個 List<T>箍鼓。
泛型約束
你可以限制泛型參數(shù)的類型范圍,這被稱為“泛型約束”呵曹。使用where關(guān)鍵字可以指定一個泛型必須滿足的一個或多個約束款咖。
fun <T> ensureTrailingPeriod(seq: T)
? ? where T : CharSequence, T : Appendable {
? ? if (!seq.endsWith('.')) {
? ? ? ? seq.append('.')
? ? }
}
val myStringBuilder = StringBuilder("Hi")
ensureTrailingPeriod(myStringBuilder)
在這個例子中,ensureTrailingPeriod 函數(shù)的類型參數(shù) T 必須同時是 CharSequence 和 Appendable 的子類型奄喂。
類型擦除和 reified 類型參數(shù)
Kotlin 中的泛型在運行時會被擦除铐殃,這意味著泛型參數(shù)的具體類型信息在運行時不可用。為了解決這個問題跨新,Kotlin 引入了reified泛型參數(shù)富腊,但它只能在內(nèi)聯(lián)函數(shù)中使用。
inline fun <reified T> isA(value: Any) = value is T
val x = 42
println(isA<Int>(x)) // 輸出 true
在這里域帐,isA函數(shù)使用reified關(guān)鍵字使泛型參數(shù)T在函數(shù)內(nèi)部實際化赘被,從而使得在運行時可以檢查value是否為T類型。
協(xié)變與逆變
Kotlin 支持泛型的協(xié)變與逆變:
協(xié)變(covariance)
允許你將子類型對象的集合賦給父類型對象的集合肖揣。在 Kotlin 中使用?out?關(guān)鍵字來表示協(xié)變民假。
逆變(contravariance)
允許你將父類型對象的集合賦給子類型對象的集合。在 Kotlin 中使用?in?關(guān)鍵字來表示逆變龙优。
協(xié)變描述了這樣一種情況:當(dāng)一個泛型類的類型參數(shù)可以接受其自己或它的子類時羊异,我們稱這個泛型類為協(xié)變的。在協(xié)變中:
- 你有一個泛型容器(比如`List<T>`),它可以持有類型`T`球化。
- 如果這個泛型容器聲明成`List<out T>`秽晚,那么你可以給它傳遞`T`或者`T`的任何子類作為類型參數(shù)。
- 這意味著如果你有`List<Animal>`筒愚,你也可以把`List<Cat>`(假設(shè)`Cat`是`Animal`的子類)當(dāng)作`List<Animal>`來使用赴蝇。
逆變是協(xié)變的反面:當(dāng)一個泛型類的類型參數(shù)可以接受其自己或它的父類時,我們稱這個泛型類為逆變的巢掺。在逆變中:
- 你有一個泛型容器或泛型函數(shù)(如`Consumer<T>`)句伶,它可以接受或操作類型`T`的輸入。
- 如果這個泛型容器或函數(shù)聲明成`Consumer<in T>`陆淀,那么你可以給它傳遞`T`或者`T`的任何父類作為類型參數(shù)考余。
- 這意味著如果你有一個專門處理`Animal`的函數(shù),你可以傳遞`Animal`或者`Animal`的任何超類(比如`Object`)給這個函數(shù)轧苫。
在協(xié)變中楚堤,泛型類容納產(chǎn)生(produce)數(shù)據(jù)的場景,你可以從中讀取數(shù)據(jù)含懊,而在適當(dāng)?shù)那闆r下身冬,子類可以代替父類。在逆變中岔乔,泛型類消費(consume)數(shù)據(jù)的場景酥筝,你可以向其寫入數(shù)據(jù),而在適當(dāng)?shù)那闆r下雏门,父類可以代替子類嘿歌。
舉個簡單的例子,如果你有一個裝蘋果的籃子茁影,這個籃子可以被看作裝水果的籃子是協(xié)變的宙帝;如果你需要一個可以接受任何水果放入的籃子,那個可以接受蘋果放入的籃子就是逆變的募闲。