一、泛型
Kotlin 中的類也可以有類型參數:
class Box<T>(t: T) {
var value = t
}
val box1: Box<Int> = Box(1)
//類型參數可以推斷出來,例如從構造函數的參數或者從其他途徑,允許省略類型參數:
val box2 = Box(1)
二、型變
聲明處型變(declaration-site variance)與類型投影(type projections)。
1.java通配符
- 上邊界通配限定符,讓 Collection<? extends Object>是 Collection<String>的超類
- 下邊界通配限定符,讓List<? super String> 是 List<Object> 的?個超類
2.聲明處型變
聲明處型變:在類型參數聲明處提供。這與 Java 的使用處型變相反。
out 修飾符:標注 Source 的類型參數 T 來確保它僅從 Source<T> 成員中返回(生產),并從不被消費。
interface Source<out T> {
fun nextT(): T
}
fun demo(strs: Source<String>) {
val objects: Source<Any> = strs // 這個沒問題,因為 T 是?個 out-參數
}
in修飾符:它使得?個類型參數逆變:只可以被消費而不可以被生產。逆變類型的?個很好的例子是 Comparable :
interface Comparable<in T> {
operator fun compareTo(other: T): Int
}
fun demo(x: Comparable<Number>) {
x.compareTo(1.0) // 1.0 擁有類型 Double,它是 Number 的?類型
val y: Comparable<Double> = x // 因此,我們可以將 x 賦給類型為 Comparable <Double> 的變量
}
三、類型投影
1.使用處型變:類型投影
val ints: Array<Int> = arrayOf(1, 2, 3)
val any = Array<Any>(3) { "" }
copy(ints, any)
fun copy(from: Array<out Any>, to: Array<Any>) { …… }
類型投影: from 不僅僅是?個數組,而是?個受限制的(投影的)數組,只可以調用返回類型為類型參數 T 的方法,如上,這意味著我們只能調用 get() 。這就是我們的使用處型變的用法, 并且是對應于 Java 的 Array<? extends Object> ,但使用更簡單些的方式。
in 投影?個類型:
fun fill(dest: Array<in String>, value: String) { …… }
Array<in String> 對應于 Java 的 Array<? super String> ,也就是說,你可以傳遞?個 CharSequence 數組或?個 Object 數組給 fill() 函數。
2.星投影
對類型參數一無所知,但仍然希望以安全的方式使用它。定義泛型類型的投影,該泛型類型的每個具體實例化將是該投影的子類型。 Kotlin 星投影語法:
- 對于 Foo <out T : TUpper> ,其中 T 是?個具有上界 TUpper 的協變類型參數,Foo <> 等價于 Foo <out TUpper> 。這意味著當 T 未知時,你可以安全地從 Foo <> 讀取 TUpper 的值。
- 對于 Foo <in T> ,其中 T 是?個逆變類型參數,Foo <> 等價于 Foo <in Nothing> 。這意味著當 T 未知時,沒有什么可以以安全的?式寫? Foo <> 。
- 對于 Foo <T : TUpper> ,其中 T 是?個具有上界 TUpper 的不型變類型參數,Foo<*> 對于讀取值 時等價于 Foo<out TUpper> ?對于寫值時等價于 Foo<in Nothing>
果泛型類型具有多個類型參數,則每個類型參數都可以單獨投影: - Function<*, String> 表示 Function<in Nothing, String>
- Function<Int, *> 表示 Function<Int, out Any?>
- Function<*, *> 表示 Function<in Nothing, out Any?> 。
四、泛型函數
不僅類可以有類型參數。函數也可以有。類型參數要放在函數名稱之前:
fun <T> singletonList(item: T): List<T> ?{
// ……
}
fun <T> T.basicToString(): String { // 擴展函數
// ……
}
五、泛型約束
能夠替換給定類型參數的所有可能類型的集合可以由泛型約束限制。
1.上界
約束類型是與 Java 的 extends 關鍵字對應的 上界,冒號之后指定的類型是上界:只有 Comparable<T> 的?類型可以替代 T
fun <T : Comparable<T>> sort(list: List<T>) { …… }
默認的上界(如果沒有聲明)是 Any? 。在尖括號中只能指定?個上界。如果同?類型參數需要多個上界,我們需 要?個單獨的 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 為泛型聲明用法執行的類型安全檢測僅在編譯期進行。運行時泛型類型的實例不保留關于其類型實參的任何信息。其類型信息稱為被擦除。例如,Foo<Bar> 與 Foo<Baz?> 的實例都會被擦除為 Foo<*>