当前位置:嗨网首页>书籍在线阅读

08-声明处型变

  
选择背景色: 黄橙 洋红 淡粉 水蓝 草绿 白色 选择字体: 宋体 黑体 微软雅黑 楷体 选择字体大小: 恢复默认

8.2.3 声明处型变

Kotlin相对于Java泛型较大的改动就是添加了声明处型变。在Java中,假设有一个泛型接口Source\,该接口中不存在任何以T作为参数的方法,该方法返回T类型值。

interface Source<T> {
  T nextT();
}

虽然从语法上来说,将Source \类型的实例引用存储到Source \类型的变量中是没有任何问题的,但是这样的操作在Java中仍然是被禁止的。

void demo(Source<String> strs) {
  Source<Object> objects = strs;   //编译错误,在Java中不允许
  //…
}

为了解决这一问题,可以声明对象的类型为 Source<? extends Object>,但这样的操作显得毫无意义。在Kotlin中,有一种方法向编译器解释这种情况,即声明处型变。对于上面的代码,使用声明处型变可以进行如下修改。

abstract class Source<out T> {
    abstract fun nextT(): T
}
fun demo(strs: Source<String>) {
    val objects: Source<Any> = strs   // 编译通过,因为T是一个out类型参数
    //…
}

实际上,Kotlin存在大量的声明处型变,比如Iterable接口的声明。

public interface Iterable<out T> {
    public operator fun iterator(): Iterator<T>
}

因为Collection接口和Map接口都继承自Iterable接口,而Iterable接口又被声明为生产者类型,所以所有的Collection对象和Map对象都可以实现安全的类型协变。代码如下。

val num:List<Number> = listOf(1,2,3)

在上面的表达式中,listOf()函数返回List\类型,因为List接口实现了安全的类型协变,所以可以安全地将List\类型的变量赋值给List\类型的变量。

除了out修饰的型变之外,Kotlin还提供了另外一种型变类型,即类型参数逆变,它使用关键字in修饰。声明的逆变只能被消费而不能被生产,逆变类的一个很好的例子是Comparable的实现。

abstract class Comparable<in T> {
    abstract fun compareTo(other: T): Int
}
fun demo(x: Comparable<Number>) {
    x.compareTo(1.0) 
    val y: Comparable<Double> = x     // 编译通过
}