Kotlin教程 第11章 泛型

泛型,即 "參數(shù)化類型",將類型參數(shù)化,可以用在類,接口,方法上。

與 Java 一樣,Kotlin 也提供泛型,為類型安全提供保證,消除類型強轉(zhuǎn)的煩惱。

聲明一個泛型類:

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

創(chuàng)建類的實例時我們需要指定類型參數(shù):

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

以下實例向泛型類 Box 傳入整型數(shù)據(jù)和字符串:

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

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

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

輸出結(jié)果為:

10
Runoob

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

Kotlin 泛型函數(shù)的聲明與 Java 相同,類型參數(shù)要放在函數(shù)名的前面:

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

在調(diào)用泛型函數(shù)時,如果可以推斷出類型參數(shù),可以省略泛型參數(shù)。

以下實例創(chuàng)建了泛型函數(shù) doPrintln,函數(shù)根據(jù)傳入的不同類型做相應處理:

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("整型數(shù)字為 $content")
        is String -> println("字符串轉(zhuǎn)換為大寫:${content.toUpperCase()}")
        else -> println("T 不是整型,也不是字符串")
    }
}

輸出結(jié)果為:

整型數(shù)字為 23
字符串轉(zhuǎn)換為大寫:RUNOOB
T 不是整型,也不是字符串

泛型約束

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

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

最常見的約束是上界(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>> 的子類型

默認的上界是 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 中沒有通配符類型,它有兩個其他的東西:聲明處型變(declaration-site variance)與類型投影(type projections)。

聲明處型變

聲明處的類型變異使用協(xié)變注解修飾符:in、out,消費者 in, 生產(chǎn)者 out。

使用 out 使得一個類型參數(shù)協(xié)變,協(xié)變類型參數(shù)只能用作輸出,可以作為返回值類型但是無法作為入?yún)⒌念愋停?/p>

// 定義一個支持協(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
}

in 使得一個類型參數(shù)逆變,逆變類型參數(shù)只能用作輸入,可以作為入?yún)⒌念愋偷菬o法作為返回值的類型:

// 定義一個支持逆變的類
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
}

星號投射

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

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

  • 假如類型定義為Foo<out T>, 其中 T 是一個協(xié)變的類型參數(shù), 上界(upper bound)為TUpper, Foo<*> 等價于 Foo<out TUpper>. 它表示, 當 T 未知時, 你可以安全地從 Foo<*> 中 讀取TUpper 類型的值
  • 假如類型定義為 Foo<in T>, 其中 T 是一個反向協(xié)變的類型參數(shù), Foo<*> 等價于 Foo<inNothing>. 它表示, 當 T 未知時, 你不能安全地向 Foo<*> 寫入 任何東西
  • 假如類型定義為 Foo<T>, 其中 T 是一個協(xié)變的類型參數(shù), 上界(upper bound)為 TUpper , 對于讀取值的場合, Foo<*> 等價于 Foo<out TUpper> , 對于寫入值的場合, 等價于 Foo<in Nothing>

如果一個泛型類型中存在多個類型參數(shù), 那么每個類型參數(shù)都可以單獨的投射. 比如, 如果類型定義為interface Function<in T, out U> , 那么可以出現(xiàn)以下幾種星號投射:

  1. Function<*, String>, 代表Function<in Nothing, String>
  2. Function<Int, *>, 代表Function<Int, out Any?>
  3. Function<*,*>, 代表Function<in Nothing, out Any?>

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


后記

關(guān)于星號投射,其實就是*代指了所有類型,相當于Any?

給文中補個例子方便理解:

class A<T>(val t: T, val t2 : T, val t3 : T)
class Apple(var name : String)
fun main(args: Array<String>) {
    //使用類    
    val a1: A<*> = A(12, "String", Apple("蘋果"))
    val a2: A<Any?> = A(12, "String", Apple("蘋果"))   //和a1是一樣的
    val apple = a1.t3    //參數(shù)類型為Any
    println(apple)
    val apple2 = apple as Apple   //強轉(zhuǎn)成Apple類
    println(apple2.name)
    //使用數(shù)組
    val l:ArrayList<*> = arrayListOf("String",1,1.2f,Apple("蘋果"))
    for (item in l){
        println(item)
    }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

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

  • 系列文章全部為本人的學習筆記,若有任何不妥之處,隨時歡迎拍磚指正。如果你覺得我的文章對你有用,歡迎關(guān)注我,我們一起...
    我愛吃栗子啊閱讀 6,793評論 5 14
  • 與Java一樣,Kotlin也支持泛型,為類型安全提供保證,消除類型強轉(zhuǎn)的煩惱 創(chuàng)建類的實例時我們需要指定類型參數(shù)...
    郎官人閱讀 1,280評論 0 0
  • Kotlin 中也有泛型的概念,和 Java 中的類似,但又不盡相同,一起來認識 Kotlin 中的泛型吧。 一、...
    SheHuan閱讀 28,634評論 7 24
  • 寫在開頭:本人打算開始寫一個Kotlin系列的教程,一是使自己記憶和理解的更加深刻,二是可以分享給同樣想學習Kot...
    胡奚冰閱讀 1,550評論 1 3
  • 泛型約束 我們可以使用泛型約束來設定一個給定參數(shù)允許使用的類型。Kotlin 中使用 : 對泛型的的類型上限進行約...
    十旋轉(zhuǎn)45度閱讀 343評論 0 0

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