Kotlin 泛型 泛型約束 型變 星號投射

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

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

聲明一個泛型類:

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

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

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

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

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)
}

輸出結果爲:

10
Runoob

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

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

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

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

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

以下實例創建了泛型函數 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 不是整型,也不是字符串

泛型約束

簡單來說就是約束某個類型

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>> 的子類型

如果想約束多個條件,可以用 where 子句:

    fun <T> copyWhenGreater(list: List<T>, threshold: T) : List<Number> where T : Number, T : Comparable<T> {
        return list.filter { it > threshold }.map { it }
    }

型變

Kotlin 中沒有通配符類型,即是以下這個:

public static void test3(List<?> c){
  for (int i = 0; i < c.size(); i++) {
       System.out.println(c.get(i));
   }
}

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

聲明處型變

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

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

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

星號投射

1.概述

<*>用來表示不知道關於泛型實參的任何信息

2.結論

<> 星型投影,修飾的容器(比如:MutableList,MutableMap ),只能讀不能寫。 相當於<out Any?>
比如:MutableList<
> 表示的是 MutableList<out Any?>

3.解釋

<*>星號投射,表示“不知道關於泛型實參的任何信息”,若在修飾容器時,因爲不知道是哪個類型,所以並不能向容器中寫入任何東西(寫入的任何值都可能會違反調用代碼的期望)。讀容易的值是可以的,因爲所有存儲在列表中的值都是Any?的子類

4.示例
    fun main() {
        val list = mutableListOf<String>()
        list.add("1")
        list.add("2")
        reportInfo(list)
    }

    fun reportInfo(info: MutableList<*>) {
        var test = Integer.valueOf(info[0].toString())
        test += 10
        tvContent.append(test.toString())
        tvContent.append("+")
        tvContent.append(info[1].toString())
    }

    fun reportInfoError(info: MutableList<*>) {
        // 這樣是編譯錯誤的
//        info.add("3")
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章