一郊酒、定義
泛型状蜗,即 "參數(shù)化類型",將類型參數(shù)化除呵,可以用在類再菊,接口,方法上颜曾。
與 Java 一樣纠拔,Kotlin 也提供泛型,為類型安全提供保證泛豪,消除類型強(qiáng)轉(zhuǎn)的煩惱稠诲。
聲明一個(gè)泛型類:
class Box<T>(t: T) {
var value = t
}
創(chuàng)建類的實(shí)例時(shí)我們需要指定類型參數(shù):
val box: Box<Int> = Box<Int>(1)
// 或者
val box = Box(1)
// 編譯器會(huì)進(jìn)行類型推斷,1的類型Int诡曙,所以編譯器知道我們說的是 Box<Int>
- 定義泛型類型變量臀叙,可以完整地寫明類型參數(shù),如果編譯器可以自動(dòng)推定類型參數(shù)价卤,也可以省略類型參數(shù)劝萤。
Kotlin 泛型函數(shù)的聲明與 Java 相同,類型參數(shù)要放在函數(shù)名的前面:
fun <T> boxIn(value: T) = Box(value)
- 在調(diào)用泛型函數(shù)時(shí)慎璧,如果可以推斷出類型參數(shù)床嫌,可以省略泛型參數(shù)
二跨释、泛型約束
我們可以使用泛型約束來設(shè)定一個(gè)給定參數(shù)允許使用的類型。
Kotlin 中使用 : 對泛型的類型上限進(jìn)行約束厌处。
最常見的約束是上界(upper bound):
fun <T : Comparable<T>> sort(list: List<T>) {
// ……
}
- Comparable 的子類型可以替代 T
- 默認(rèn)的上界是 Any?鳖谈。
- 對于多個(gè)上界約束條件,可以用 where 子句:
fun <T> copyWhenGreater(list: List<T>, threshold: T): List<String>
where T : CharSequence,
T : Comparable<T> {
return list.filter { it > threshold }.map { it.toString() }
}
三阔涉、型變
Kotlin 中沒有通配符類型缆娃,它有兩個(gè)其他的東西:
- 聲明處型變(declaration-site variance)
- 類型投影(type projections)
1、聲明處型變
聲明處的類型變異使用協(xié)變注解修飾符:in洒敏、out龄恋,消費(fèi)者 in, 生產(chǎn)者 out。
- 使用 out 使得一個(gè)類型參數(shù)協(xié)變凶伙,協(xié)變類型參數(shù)只能用作輸出郭毕,可以作為返回值類型但是無法作為入?yún)⒌念愋停?/li>
- in 使得一個(gè)類型參數(shù)逆變,逆變類型參數(shù)只能用作輸入函荣,可以作為入?yún)⒌念愋偷菬o法作為返回值的類型:
// 定義一個(gè)支持協(xié)變的類
class Runoob<out A>(val a: A) {
fun foo(): A {
return a
}
}
fun main(args: Array<String>) {
var strCo: Runoob<String> = Runoob("a")
var anyCo: Runoob<Any> = Runoob<Any>("b")
anyCo = strCo
println(anyCo.foo()) // 輸出 a
}
// 定義一個(gè)支持逆變的類
class Runoob<in A>(a: A) {
fun foo(a: A) {
}
}
fun main(args: Array<String>) {
var strDCo = Runoob("a")
var anyDCo = Runoob<Any>("b")
strDCo = anyDCo
}
2显押、類型投影
有些時(shí)候, 你可能想表示你并不知道類型參數(shù)的任何信息, 但是仍然希望能夠安全地使用它. 這里所謂"安全地使用"是指, 對泛型類型定義一個(gè)類型投射, 要求這個(gè)泛型類型的所有的實(shí)體實(shí)例, 都是這個(gè)投射的子類型。
對于這個(gè)問題, Kotlin 提供了一種語法, 稱為 星號投射(star-projection):
- 假如類型定義為 Foo<out T> , 其中 T 是一個(gè)協(xié)變的類型參數(shù), 上界(upper bound)為 TUpper ,Foo<> 等價(jià)于 Foo<out TUpper> . 它表示, 當(dāng) T 未知時(shí), 你可以安全地從 Foo<> 中 讀取TUpper 類型的值.
- 假如類型定義為 Foo<in T> , 其中 T 是一個(gè)反向協(xié)變的類型參數(shù), Foo<> 等價(jià)于 Foo<inNothing> . 它表示, 當(dāng) T 未知時(shí), 你不能安全地向 Foo<> 寫入 任何東西.
- 假如類型定義為 Foo<T> , 其中 T 是一個(gè)協(xié)變的類型參數(shù), 上界(upper bound)為 TUpper , 對于讀取值的場合, Foo<*> 等價(jià)于 Foo<out TUpper> , 對于寫入值的場合, 等價(jià)于 Foo<in Nothing> .
如果一個(gè)泛型類型中存在多個(gè)類型參數(shù), 那么每個(gè)類型參數(shù)都可以單獨(dú)的投射. 比如, 如果類型定義為interface Function<in T, out U> , 那么可以出現(xiàn)以下幾種星號投射:
- Function<*, String> , 代表 Function<in Nothing, String> ;
- Function<Int, *> , 代表 Function<Int, out Any?> ;
- Function<, > , 代表 Function<in Nothing, out Any?> .
注意: 星號投射與 Java 的原生類型(raw type)非常類似, 但可以安全使用