Kotlin 基础:泛型

47 篇文章 0 订阅
10 篇文章 0 订阅

本文介绍 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())
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值