Kotlin中的泛型


概念

如果A,B表示类型,f(*)表示类型转换, <= 表示继承关系(如果 A <= B,则表示A是B的子类)

  • 里氏替换原则(LSP)

    子类对象可以在程序中替换基类对象,”SOLID”原则中的L就是这个.

  • 协变

    如果f(*)是协变的,当 A <= B时,f(A) <= f(B)成立

  • 逆变

    如果f(*)是协变的,当 A <= B时,f(B) <= f(A)成立

  • 不变

    如果f(*)是不变的,当A <= B时,f(A)和f(B)没有继承关系

比如f(A) = ArrayList<A>, f(B) = ArrayList<B>:

Java

先看一下下面这行代码:

 ArrayList<Number> list = new ArrayList<Integer>();
 ArrayList<Integer> list1 = new ArrayList<Number>();

很明显,在Java中这样写是错误的.因为ArrayList<>是不变的,因此两个类并没有继承关系,不能这么写

在Java中泛型是不变的,因此f(A)和f(B)没有继承关系.

在Java中数组是协变的,f(A) <= f(B)

对于泛型来说,有时候需要协变和逆变怎么办?

  • 泛型协变 : <? extends ClassA>

    ArrayList<? extends Number> list = new ArrayList<Integer>();
  • 泛型逆变: <? super ClassA>

    ArrayList<? super Integer> list = new ArrayList<Number>();

<? extends ClassA>决定了泛型的上界,即基类是ClassA,匹配的类只能是ClassA或者ClassA的子类

<? super ClassA>决定了泛型的下界,即子类是ClassA,匹配的类只能是ClassA或者ClassA的父类.

并且有一个辅助记忆的词: PECS = Producer-extends Consumer-super

只能从中进行读取操作的对象是生产者,只能向其中写入数据的对象是消费者

也就是说extends只允许读操作,super只允许写操作,读写都要则不要使用通配符

Kotlin

Kotlin中的泛型与Java类似,一般是不型变的,不过也有修饰符分别对应协变和逆变:

  • out

    相当于Java 中的extends,协变,只允许从中读取数据,不允许写

  • in

    相当于Java中的super,逆变,只允许向其中写数据,不允许读

Kotlin是在泛型声明的时候就使用型变的,因此叫做声明处型变

Kotlin还有一种叫做星投影的东西,即Foo<*>,每个具体的实例化都是该投影的自类型.而且泛型擦除都会擦为Foo<*>

至于Kotlin泛型的上下界约束问题:

  • 上界

    open class Animal<T : Comparable<T>>() {}

    用一个冒号即可表示,如果没有指定,则默认是Any?

    如果需要使用多个上界(在尖括号中只能指定一个上界),则需要使用where修饰符:

    fun <T> demo(list: List<T>, threshold: T):
            List<String>
            where T : CharSequence,
                  T : Comparable<T> {
        return list.filter { it > threshold }.map { it.toString() }
    }

    所传递的类型必须同时满足where语句的所有条件,比如上面的例子,类型 T 必须实现了 CharSequence 实现了 Comparable

参考文章

  1. https://www.cnblogs.com/en-heng/p/5041124.html
  2. https://www.kotlincn.net/docs/reference/generics.html

文章作者: 姜康
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 姜康 !
评论
 上一篇
Android通知的简单用法 Android通知的简单用法
由于不同版本API兼容性问题,我们通常使用NotificationCompact去创建通知. 注册NotificationChannel一般在应用启动的时候注册通知channel: private const val channel
2020-10-25
下一篇 
Kotlin中的inline Kotlin中的inline
inline/noinline/crossinline/reified内联,这个概念无需多说. kotlin中使用高阶函数在运行时会有一些性能损失:每一个函数都是一个对象,并且都会捕获一个闭包(哪些在函数体内可以访问到的变量),对函数对象和
2020-10-14
  目录