Kotlin學習之 12泛型

Kotlin 泛型

泛型,即 “參數化類型”,將類型參數化,可以用在類,接口,方法上。

與 Java 一樣,Kotlin 也提供泛型,爲類型安全提供保證,消除類型強轉的煩惱。

一.聲明一個泛型類:


class Box<T>(t: T) {
    var value = t
}

1.創建類的實例時我們需要指定類型參數:


val box: Box<Int> = Box<Int>(1)
// 或者
val box = Box(1) // 編譯器會進行類型推斷,1 類型 Int,所以編譯器知道我們說的是 Box<Int>。

2.以下實例向泛型類 Box 傳入整型數據和字符串:


class Box<T>(t : T) {
    var value = t
}

fun main(args: Array<String>) {
    var boxInt = Box<Int>(10)//等同於 var boxInt = Box(10)
    var boxString = Box<String>("Runoob")

    println(boxInt.value)
    println(boxString.value)
}

輸出結果爲:

10
Runoob

定義泛型類型變量,可以完整地寫明類型參數,如果編譯器可以自動推定類型參數,也可以省略類型參數。

3.Kotlin 泛型方法的聲明與 Java 相同,類型參數要放在方法名的前面:


fun <T> boxIn(value: T) = Box(value)

// 以下都是合法語句
val box4 = boxIn<Int>(1)
val box5 = boxIn(1)     // 編譯器會進行類型推斷

4.在調用泛型方法時,如果可以推斷出類型參數,可以省略泛型參數。

以下實例創建了泛型方法 doPrintln,方法根據傳入的不同類型做相應處理:

fun main(args: Array<String>) {
    val age = 23
    val name = "runoob"
    val bool = true

    doPrintln(age)    // 整型
    doPrintln(name)   // 字符串
    doPrintln(bool)   // 布爾型
}

fun <T> doPrintln(content: T) {

    when (content) {
        is Int -> println("整型數字爲 $content")
        is String -> println("字符串轉換爲大寫:${content.toUpperCase()}")
        else -> println("T 不是整型,也不是字符串")
    }
}

輸出結果爲:


整型數字爲 23
字符串轉換爲大寫:RUNOOB
T 不是整型,也不是字符串

二.泛型約束

我們可以使用泛型約束來設定一個給定參數允許使用的類型。

Kotlin 中使用 : 對泛型的的類型上限進行約束。

1.最常見的約束是上界(upper bound):


fun <T : Comparable<T>> sort(list: List<T>) {
    // ……
}

Comparable 的子類型可以替代 T。 例如:


sort(listOf(1, 2, 3)) // OK。Int 是 Comparable<Int> 的子類型
sort(listOf(HashMap<Int, String>())) // 錯誤:HashMap<Int, String> 不是 Comparable<HashMap<Int, String>> 的子類型

2.默認的上界是 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() }
}

上面泛型約束看得我真的是一臉懵逼!
對於這些陌生的代碼和說明,如果沒有運行實例真是不想去想。會有不少想不通的地方!
可能也是因爲我在java中使用泛型的實例不多!特別是多條件約束基本沒自己寫過!

這裏只給一個泛型約束的簡單示例:

3.泛型約束示例

//泛型約束 <佔位符:類型>
fun <T : Comparable<T>> sort(list: List<T>) {
    // ……
    println("sort method:"+list[0])
    //叫我寫排序還可以,但是叫我寫判斷list裏面的數據類型,我也不知道怎麼搞!
    //如果只是用is判斷好像不行!還會報錯!
}

 fun main(args: Array<String>) {
        val list=listOf(1,2,3,8,4)
        sort(list)
        println(list)

        val list2=listOf("hello","world")
        sort(list2)
        println(list2)

}

代碼運行結果:

sort method:1
[1, 2, 3, 8, 4]
sort method:hello
[hello, world]

三.型變

Kotlin 中沒有通配符類型,它有兩個其他的東西:聲明處型變(declaration-site variance)與類型投影(type projections)。

1.聲明處型變

聲明處的類型變異使用協變註解修飾符:in、out,消費者 in, 生產者 out。

使用 out 使得一個類型參數協變,協變類型參數只能用作輸出,可以作爲返回值類型但是無法作爲入參的類型:


// 定義一個支持協變的類
class Runoob<out A>(val a: A) {
    fun foo(): A { //out類型不能作爲方法的參數
        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
}

in 使得一個類型參數逆變,逆變類型參數只能用作輸入,可以作爲入參的類型但是無法作爲返回值的類型:


// 定義一個支持逆變的類
class Runoob<in A>(a: A) {
    fun foo(a: A) {   //in類型能做參數,不能作爲返回的值
        println(a)    //這裏a對象是參數對象,不是類的a對象,並且這個方法裏面得不到類的參數對象a,不知道爲啥?
    }
}

fun main(args: Array<String>) {
    var strDCo = Runoob("a")
    var anyDCo = Runoob<Any>("b")
    strDCo = anyDCo
    strDCo.foo("bb") //最終輸出bb
}

這裏,我並沒有感覺到in、out有啥大的作用和意義。。。

2.星號投射

有些時候, 你可能想表示你並不知道類型參數的任何信息, 但是仍然希望能夠安全地使用它.
這裏所謂”安全地使用”是指, 對泛型類型定義一個類型投射, 要求這個泛型類型的所有的實體實例, 都是這個投射的子類型。

對於這個問題, Kotlin 提供了一種語法, 稱爲 星號投射(star-projection):

假如類型定義爲 Foo , 其中 T 是一個協變的類型參數, 上界(upper bound)爲 TUpper ,Foo<> 等價於 Foo . 它表示, 當 T 未知時, 你可以安全地從 Foo<> 中 讀取TUpper 類型的值.
假如類型定義爲 Foo , 其中 T 是一個反向協變的類型參數, Foo<> 等價於 Foo . 它表示, 當 T 未知時, 你不能安全地向 Foo<> 寫入 任何東西.
假如類型定義爲 Foo , 其中 T 是一個協變的類型參數, 上界(upper bound)爲 TUpper , 對於讀取值的場合, Foo<*> 等價於 Foo , 對於寫入值的場合, 等價於 Foo .
如果一個泛型類型中存在多個類型參數, 那麼每個類型參數都可以單獨的投射.

比如, 如果類型定義爲interface Function


Function<*, String> , 代表 Function<in Nothing, String> ;
Function<Int, *> , 代表 Function<Int, out Any?> ;
Function<, > , 代表 Function<in Nothing, out Any?> .

注意: 星號投射與 Java 的原生類型(raw type)非常類似, 但可以安全使用

本文介紹了關於Kotlin的泛型,後面有些知識還是比較難懂!
鑑於java使用泛型都是不是很多,建議大家讀懂上面第一大點的內容:泛型的聲明,就可以了。

上面的示例代碼也可以在線測試的:https://blog.csdn.net/wenzhi20102321/article/details/79859347

共勉:苦難能提高我們生命的質量。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章