泛型

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ù)的語法。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 前言 人生苦多,快來 Kotlin ,快速學(xué)習(xí)Kotlin! 什么是Kotlin? Kotlin 是種靜態(tài)類型編程...
    任半生囂狂閱讀 26,669評論 9 118
  • 泛型代碼可以確保你寫出靈活的,可重用的函數(shù)和定義出任何你所確定好的需求的類型。你的可以寫出避免重復(fù)的代碼,并且用一...
    iOS_Developer閱讀 880評論 0 0
  • 不知何時開始流行:人生像是一場修行,追尋更好的自己。然而可笑的是,勾起的完美微笑下, 卻是丟失的自己。
    舒泠閱讀 336評論 0 0
  • 大家好,我是鷹子。 今天我給大家分享的是一本非常實用的溝通工具書《溝通圣經(jīng)》。這本書是英國的尼基·斯坦頓寫的,截止...
    鷹子的筆記本閱讀 378評論 0 0
  • 懂得保護(hù)自己的界限,懂得欣賞自己的性別,懂得在青春期合適的面對性沖動,并且等候。這便是我們對子女性教育的最大目的。...
    溪邊小樹閱讀 206評論 1 0

友情鏈接更多精彩內(nèi)容