1.kotlin和java一樣支持類型參數(shù):
class Box<T>(t: T) {
var value = t
}
你可以像下面這樣創(chuàng)建實例,你需要提供類型參數(shù):
val box: Box<Int> = Box<Int>(1)
假如類型參數(shù)是可以推斷的,比如根據(jù)構(gòu)造函數(shù)的參數(shù)等等,類型參數(shù)是可以忽略的,如下所示:
val box = Box(1) // 1 has type Int, so the compiler figures out that we are talking about Box<Int>
2.型變
Java 類型系統(tǒng)中最棘手的部分之一是通配符類型,如果不了解java通配符的同學(xué)可以先了解 java通配符的使用,而在kotlin中沒有通配符,為了解決java中遇到的問題,提出了另外兩個東西:聲明處型變(declaration-site variance)與類型投影(type projections)
3.聲明處型變
聲明處形變out、in
out協(xié)變的類型參數(shù)示例:
open class Animal {
}
open class Suckler: Animal() {
}
class Dog: Suckler() {
}
class Source<out T> {
fun get():T? {
return null
}
//out 聲明的類型參數(shù)不能作為參數(shù)傳入,否則編譯不通過
// fun add(t: T) {
//
// }
//只能將Suckler或其子類賦值給t
//好比我有一個只能往外取Suckler的箱子,你把這個箱子換成裝Dog的箱子也是沒問題的,因為你取出來的Dog可以當(dāng)Suckler使用
fun match(source: Source<Dog>) {
var t: Source<Suckler> = source
// val dog: Dog = t.get()
}
//out聲明的泛型只能將String或者子類賦值給t,下面編譯不通過
// fun mismatch(tree: Source<Animal>) {
// var t: Source<Dog> = tree
// }
}
- out修飾的類型參數(shù)是生產(chǎn)者如Source<out T>,只允許從source中返回T(即生產(chǎn)),而不能添加(作為參數(shù)傳入)
- 可以將子類賦值給父類
- Array<out Any>相當(dāng)于java的Array<? extends Object>
in:逆變的類型參數(shù)示例:
class Sink<in T> {
fun add(t: T) {
}
//in修飾的類型參數(shù)不能返回,否則會編譯不通過
// fun get() : T? = null
//只能將Suckler類型或其父類賦值給 t
//好比我有一個只能往里面放Suckler的箱子,你把它換成可以裝Animal的箱子也是沒問題的,因為Suckler和Dog都可以往里面放
fun match(sink: Sink<Animal>) {
var t: Sink<Suckler> = sink
t.add(Suckler())
t.add(Dog())
// t.add(Animal)
}
//編譯不通過,報Type mismatch
// fun dismiss(router: Sink<Suckler>) {
// var t: Sink<Animal> = router
// }
}
- 和out相反in修飾的參數(shù)類型只能被消費(可以作為參數(shù)傳入)而不可以被生產(chǎn)(返回)
- 可以將父類賦值給子類
- Array<in String> 相當(dāng)于java的 Array<? super String>
4.使用處型變
考慮下面的問題:
class Array<T>(val size: Int) {
fun get(index: Int): T { ///* …… */ }
fun set(index: Int, value: T) { ///* …… */ }
}
fun copy(from: Array<Any>, to: Array<Any>) {
assert(from.size == to.size)
for (i in from.indices)
to[i] = from[i]
}
這個函數(shù)應(yīng)該將項目從一個數(shù)組復(fù)制到另一個數(shù)組。讓我們嘗試在實踐中應(yīng)用它:
val ints: Array<Int> = arrayOf(1, 2, 3)
val any = Array<Any>(3) { "" }
copy(ints, any) // 錯誤:期望 (Array<Any>, Array<Any>)
我們可以在使用初形變解決這個問題:
fun copy(from: Array<out Any>, to: Array<Any>) {
// ......
}
類型投影:這里的 from 不僅僅是一個數(shù)組,而是一個受限制的(投影 的)數(shù)組。
或者使用 in 做類型投影:
fun fill(dest: Array<in String>, value: String) {
// ......
}
Array<in String> 對應(yīng)于 Java 的 Array<? super String> ,也就是說,你可以傳遞一個 CharSequence 數(shù)組或一個 Object 數(shù)組給 fill() 函數(shù)。
5. 星投影
class Bar<in T, out U>() {
fun add(t: T) {
}
fun get(): U? {
return null
}
}
fun test0(bar: Bar<*, Suckler>) {
//添加不了,因為我不知道你的類型是什么
// bar.add(Animal())
}
fun test1(bar: Bar<Suckler, *>) {
bar.add(Suckler())
bar.add(Dog())
bar.get()
}
fun test2(bar: Bar<*, *>) {
//添加不了,因為我不知道你的類型是什么
// bar.add(Animal())
bar.get()
}
你對類型參數(shù)一無所知,但仍然希望以安全的方式使用它。 這里的安全方式是 定義泛型類型的這種投影,該泛型類型的每個具體實例化將是該投影的子類型。
星投影語法:
假設(shè)類型被聲明 為 interface Function <in T, out U> ,我們可以想象以下星投影:
-
Function<*, String>表示Function<in Nothing, String>; -
Function<Int, *>表示Function<Int, out Any?>; -
Function<*, *>表示Function<in Nothing, out Any?>。
6.泛型函數(shù)
fun <T> singletonList(item: T): List<T> {
// ......
}
fun <T> T.basicToString() : String { // 擴展函數(shù)
// ......
}
val l = singletonList<Int>(1)
7.泛型約束
fun <T : Comparable<T>> sort(list: List<T>) {
// ......
}
冒號之后指定的類型是上界:只有Comparable<T> 的子類型可以替代 T 。
8.具體化的類型參數(shù)
內(nèi)聯(lián)函數(shù)支持具體化的類型參數(shù)。下面看看我們不使用具體化的類型參數(shù)的情況:
fun <T> TreeNode.findParentOfType(clazz: Class<T>): T? {
var p = parent
while (p != null && !clazz.isInstance(p)) {
p = p.parent
}
@Suppress("UNCHECKED_CAST")
return p as T?
}
treeNode.findParentOfType(MyTreeNode::class.java)
上面的代碼使用了擴展函數(shù)的語法,如對擴展函數(shù)不熟悉,請查看擴展函數(shù)的語法。
讓我們看看下面使用具體化的類型參數(shù)的例子:
inline fun <reified T> TreeNode.findParentOfType(): T? {
var p = parent
while (p != null && p !is T) {
p = p.parent
}
return p as T?
}
myTree.findParentOfType<MyTreeNodeType>()
我們使用 reified 修飾符來限定類型參數(shù),現(xiàn)在可以在函數(shù)內(nèi)部訪問它了, 幾乎就像是一個 普通的類一樣。由于函數(shù)是內(nèi)聯(lián)的,不需要反射,正常的操作符如 !is 和 as 現(xiàn)在都能用了。如對內(nèi)聯(lián)函數(shù)不熟悉,請查看內(nèi)聯(lián)函數(shù)的語法。