Kotlin 基礎:泛型

本文介紹 Kotlin 中的泛型,可與 Java 基礎:泛型 配合食用。

一、爲什麼要有泛型

  • 效率、成本
  • 減少樣板代碼的編寫

二、泛型的分類

  • 泛型類
  • 泛型方法

三、泛型的關鍵字

3.1 T

T 代表任意一個類型,可以爲任意字符串,一般爲:T、U、S、E、K、V。

  • T、U、S:任意類型
  • E:集合的元素類型
  • K、V:Map 的 key 和 value 類型

3.2 out

通配符,相當於 Java 的 ? extend

3.3 in

通配符,相當於 Java 的 ? super

四、泛型類、方法的定義

4.1 泛型類的定義

class AClass<T> ...
class AClass<out T> ...
class AClass<in T> ...

<T> 在類名後聲明。在該類中,可以

  • 使用 T 作爲對象的類型聲明

    val a: T;

  • 使用 T 作爲泛型類型對象的類型參數

    List<T> list;

  • 使用 T 作爲方法返回類型前的聲明(所以泛型類中的泛型方法可以省去返回類型前的 )

    見泛型方法的定義

4.2 泛型方法的定義

fun <T> ...

<T> 在方法返回類型前聲明。在該方法中,可以

  • 使用 T 作爲方法的返回類型

    fun <T> foo() : T …

  • 使用 T 作爲方法的參數類型

    fun <T> foo(t: T) …

  • 使用 T 作爲方法的泛型參數的類型參數

    fun <T> foo(list: List<T>) …

五、泛型類、方法的使用

使用即泛型類的實例化。

5.1 泛型實現類

指類型參數指定爲一個確定的單一類,如 String 對應 AClass<String>,它表示:

一個泛型實現類,類型參數是 String(即 AClass<T>,使用時 T 已經被指定爲 String)。

5.2 通配類

5.2.1 AClass< out BClass >

它代表一批泛型實現類,這批實現類的特點是:

  • 它們的類型參數只能是 BClass 的子類。
  • 它們自己,都是 AClass<out BClass> 的子類。

這種泛型實現類通配類的父子關係,和泛型實現類的類型參數通配類的類型參數的父子關係相同的關係,稱爲協變

當實現類轉爲 AClass<out BClass> 時:

  • 可以 get 到 BClass,因爲所有實現類的類型參數的共同上界就是 BClass。
  • 不能 set 任何值,因爲不確定實現類的類型參數是哪個 BClass 的子類。

5.2.2 AClass< in BClass >

它代表一批泛型實現類,這批實現類的特點是:

  • 它們的類型參數只能是 BClass 的父類。
  • 它們自己,都是 AClass 的子類。

這種泛型實現類通配類的父子關係,和泛型實現類的類型參數通配類的類型參數的父子關係相反的關係,稱爲逆變

當實現類轉爲 AClass 時:

  • 不能 get 到 BClass,只能 get 到 Object,因爲所有實現類的類型參數的共同上界只有 Object。
  • 可以 set BClass 及其子類,因爲一定是覆蓋真正的實現類的類型參數的。

5.2.3 類定義中的 out、in

Kotlin 中爲了方便開發,out、in 除了可以在使用時出現,也可以放在定義時,如 class AClass<out T>。

class AClass<T> 的定義 + AClass<out BClass> 的使用相當於
class AClass<out T> 的定義 + AClass<BClass> 的使用。

由於 out、in 的協變、逆變特性,出現在定義裏時,Kotlin 會對類做一些限制:

  • out 的泛型類不允許 T 作爲方法的參數,除非加上 @UnsafeVariance 的註解。(只能 get,不能 set)
  • in 的泛型類不允許 T 作爲返回類型。(只能 set,不能 get)

六、代碼示例

6.1 out、in 在使用中時

class ChangeKotlin {
    open class A
    open class B : A()
    open class C : B()

    class AClass<T> {
        private var mT: T? = null
        fun get(): T? {
            return mT
        }

        fun set(t: T) {
            mT = t
        }
    }

    fun <T> d(list: List<T>) {

    }

    fun main() {
        val aClass: AClass<String> = AClass()
        var aClass1: AClass<Any> = AClass()
        aClass1 = aClass // 報錯

        val aClassA: AClass<A> = AClass()
        val aClassB: AClass<B> = AClass()
        val aClassC: AClass<C> = AClass()

        val aClassExtendB1: AClass<out B> = aClassA // 報錯
        val aClassExtendB2: AClass<out B> = aClassB
        val aClassExtendB3: AClass<out B> = aClassC
        val b2: B? = aClassExtendB2.get() // 可以 get 到 B
        aClassExtendB2.set() // 報錯,set 任何值都會報錯

        val aClassSuperB1: AClass<in B> = aClassA
        val aClassSuperB2: AClass<in B> = aClassB
        val aClassSuperB3: AClass<in B> = aClassC // 報錯
        val o: Any? = aClassSuperB1.get() // 只能 get 到 Any
        aClassSuperB1.set(A()) // 報錯,set 除 B 及其子類以外的類型就會報錯
        aClassSuperB1.set(B())
        aClassSuperB1.set(C())
    }
}

6.2 out、in 在定義中時

class ChangeKotlin2 {
    open class A
    open class B : A()
    open class C : B()

    class AClassOut<out T> {
        private var mT: T? = null
        fun get(): T? {
            return mT
        }

        // 報錯,T 不允許作爲參數,除非加上 @UnsafeVariance 的註解
        //fun set(t: T) {
        //    mT = t
        //}
    }

    class AClassIn<in T> {
        private var mT: T? = null
        // 返回類型寫 T? 會報錯,不允許 T 作爲返回類型
        fun get(): Any? {
            return mT
        }

        fun set(t: T) {
            mT = t
        }
    }

    fun main() {
        val aClass: AClassOut<String> = AClassOut()
        var aClass1: AClassOut<Any> = AClassOut()
        aClass1 = aClass // 正常

        val aClassAOut: AClassOut<A> = AClassOut()
        val aClassBOut: AClassOut<B> = AClassOut()
        val aClassCOut: AClassOut<C> = AClassOut()

        val aClassExtendB1: AClassOut<B> = aClassAOut // 報錯
        val aClassExtendB2: AClassOut<B> = aClassBOut
        val aClassExtendB3: AClassOut<B> = aClassCOut
        val b2: B? = aClassExtendB2.get() // 可以 get 到 B
        aClassExtendB3.set(B()) // 報錯,在定義期間就限制了沒有這樣的方法

        val aClassAIn: AClassIn<A> = AClassIn()
        val aClassBIn: AClassIn<B> = AClassIn()
        val aClassCIn: AClassIn<C> = AClassIn()

        val aClassSuperB1: AClassIn<B> = aClassAIn
        val aClassSuperB2: AClassIn<B> = aClassBIn
        val aClassSuperB3: AClassIn<B> = aClassCIn // 報錯
        val o: Any? = aClassSuperB1.get() // 只能 get 到 Any
        aClassSuperB1.set(A()) // 報錯,set 除 B 及其子類以外的類型就會報錯
        aClassSuperB1.set(B())
        aClassSuperB1.set(C())
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章