Kotlin泛型

作者: agile4j | 来源:发表于2018-09-01 20:18 被阅读90次

作者:刘仁鹏
参考资料:
1.https://www.yiibai.com/kotlin/generics.html
2.https://www.jianshu.com/p/832b9548b331


  • 注:为更好地理解Kotlin中泛型的用法,文中会与Java的泛型机制做对比。默认读者对Java的泛型有基本了解。

1.型变的概念

  • 型变分两种:协变逆变

  • F类型是C类型的父类型,我们简记为F<-C。则有:

    协变 :当F<-C时,有f(F)<-f(C),则称f是协变的
    逆变 : 当F<-C时,有f(C)<-f(F),则称f是逆变的

  • Java中的泛型是 不型变的,即List<String>并 不是 List<Object>的子类型。

  • 为解决这个问题,Java引入了 通配符 的概念,来提供对 型变 的支持:

//List<Object> list = new ArrayList<String>(); //编译期错误:Incompatible types.
List<? extends Object> list = new ArrayList<String>(); //OK
List<? super String> list2 = new ArrayList<Object>(); //OK
  • Java中的 < ? extends T>实现了泛型的协变

  • Java中的 < ? super T>实现了泛型的逆变

  • Kotlin中没有 通配符 的概念,它通过另外两个概念实现了对 型变 的支持:声明处型变类型投影 (后面详细介绍)

2.PECS

  • Java中的extends和super关键字,被用在什么场景下呢?《Effective Java》中已经给出了答案:

    PECS :producer-extends, customer-super

  • PECS的原因:extends 规定了类型的上限,它被用来作为 生产者 的角色。因为它 能保证该通配符代表的具体类型,可被安全的转型为类型上限,因此作为方法的 返回类型 是安全的。而如果被当做 消费者 ,则不安全:因为它 不能保证该通配符代表的具体类型,到底是类型上限的哪个子类型,所以它不能用来作为方法的 参数类型。与此同理,super 只能被用来作为 消费者 。一个典型的CASE:

public static <T> void copy(List<? super T> dest, List<? extends T> src) {
    // 校验略
    ListIterator<? super T> di = dest.listIterator();
    ListIterator<? extends T> si = src.listIterator();
    while (si.hasNext()) {
        di.next();
        T element = si.next(); // src extent 生产者 作为方法返回值类型是安全的
        di.set(element); // dest super 消费者 作为方法参数类型是安全的
    }
}
  • 注意:虽然被extends通配符修饰的对象,是 生成者 的角色,但这并不意味着该对象是 不可变的 :例如你仍然可以调用对象的clear()方法来删除对象内持有的所有元素,因为clear()根本无需任何参数。通配符 保证的唯一事情是 类型安全不可变性 完全是另一回事。

3.声明处型变

  • 上文提到Kotlin没有通配符的概念,而是通过另两个概念来实现型变。本小节介绍这两个概念中的 声明处型变,下一节讲解另一个概念:类型投影
  • 声明处型变 是指可在泛型 被声明的地方 指定该泛型的 型变属性(协变/逆变) (Java只可在泛型 被使用的地方 制定型变属性)
  • Kotlin的声明处型变通过 out修饰符 表示该泛型是 协变 的,只能被用来当做 生产者,只能出现在 输出位置。而 in修饰符 表示该泛型是 逆变 的,只能被用来当做 消费者,只能出现在 输入位置。例如:
abstract class Supplier<out T> {
    abstract fun get(): T
    //abstract fun set(t: T) 
    //编译期错误:Type parameter T is declared as 'out' but occurs in 'in' position in type T
}

abstract class Customer<in T> {
    abstract fun set(t: T)
    //abstract fun get(): T 
    //编译期错误:Type parameter T is declared as 'in' but occurs in 'out' position in type T
}
  • 声明处型变 带来的 好处 是:当一个类 C 的类型参数 T 被声明为 out 时,则C<Base>可安全地作为C<Derived>的 超类。也可说:

    1. C 在泛型 T 上是 协变的
    2. T 是一个协变的 类型参数
    3. CT生产者,而不是 T消费者
  • out 和 in 修饰符称为 型变注解,并且由于它在类型参数 声明处 提供,所以才叫作 声明处型变

4.类型投影

  • 类型投影 是Kotlin中的 使用处型变
  • 类型投影 存在的必要性:声明式投影 非常方便,但 无法解决一个类既是泛型T的生产者,又是泛型T的消费者的情况,例如:
abstract class Container<T>(val size: Int) {
    abstract fun get(index: Int): T
    abstract fun set(index: Int, value: T)
}
  • 上述Array类在泛型T上是 不型变 的,即Array<Any>和Array<Int>都不是另一个的子类型。
  • 因此Kotlin仍然需要 使用处型变 的用法,即 类型投影 :被类型投影修饰的 对象,是一个 受限制的(投影的)对象out 修饰的情况下,只可以调用其 返回类型 为类型参数T的方法;in 修饰的情况下,只可以调用其 参数类型 为类型参数T的方法。例如:
fun copy(from: Array<out Any>, to: Array<Any>) {
    // 校验略
    for (i in from.indices) {
        to[i] = from[i]
        //from[i] = null 
        //编译期错误:Out-projected type 'Array<out Any>' prohibits the use of 'public final operator fun set(index: Int, value: T): Unit defined in kotlin.Array
    }
}
  • 如上,Kotlin中虽然没有 通配符 的概念,但是 类型投影 起到了与之相同的作用:Array<out String> 对当于Java的Array<? extends String>,Array<in String> 对当于Java的Array<? super String>

5.星投影

  • Kotlin中的星投影跟Java中的原始类型类似,但星投影是安全的。
  • 如果类型被声明为interface Function<in T, out U>,则有以下星投影:
  1. Function< *, String >表示Function< in Nothing, String >
  2. Function< Int, * >表示Function< Int, out Any? >
  3. Function< *, * >表示Function< in Nothing, out Any? >

end

相关文章

  • 泛型

    与Java泛型相同,Kotlin同样提供了泛型支持。对于简单的泛型类、泛型函数的定义,Kotlin 与 Java ...

  • Kotlin---泛型

    Kotlin不变型泛型 Kotlin的不变型泛型和Java一样,通过声明泛型类型来使用泛型类。而该种泛型声明后,则...

  • Kotlin 泛型 VS Java 泛型

    建议先阅读我的上一篇文章 -- Java 泛型 和 Java 泛型一样,Kotlin 泛型也是 Kotlin 语言...

  • Kotlin for android学习六:泛型

    前言 kotlin官网和kotlin教程学习教程的笔记。 1. 声明泛型 2. 泛型约束 : 对泛型的类型上限进行...

  • 泛型

    Kotlin 泛型详解 声明一个泛型类 声明一个泛型方法 泛型约束 List 和 List 是...

  • Kotlin 泛型

    Kotlin 支持泛型, 语法和 Java 类似。例如,泛型类: 泛型函数: 类型变异 Java 的泛型中,最难理...

  • Kotlin:泛型杂谈(下)

    在Kotlin:泛型杂谈(上)中,从泛型扩展属性、非空约束、实例化类型参数三个方面简单介绍了一下Kotlin中泛型...

  • 【Android】 Kotlin(七)泛型

    深入理解Kotlin泛型 Kotlin 的泛型与 Java 一样,都是一种语法糖,即只在源代码中有泛型定义,到了c...

  • Kotlin 泛型

    说起 kotlin 的泛型,就离不开 java 的泛型,首先来看下 java 的泛型,当然比较熟悉 java 泛型...

  • Kotlin 泛型

    Kotlin 泛型 1. 泛型类 定义一个泛型类 使用 在继承中 使用 2. 泛型函数 使用 3. 泛型的擦除 无...

网友评论

    本文标题:Kotlin泛型

    本文链接:https://www.haomeiwen.com/subject/itcmmftx.html