Kotlin学习之泛型

作者: 程序员丶星霖 | 来源:发表于2017-12-05 08:43 被阅读134次

Kotlin学习之泛型

Kotlin的泛型与Java一样,都是一种语法糖,只在源代码里出现,编译时会进行简单的字符替换。

泛型其实就是类型参数,它给强类型编程语言加入了更强的灵活性。

在Java中,只要是有类型的元素,都可以泛型化。泛型类、泛型接口、泛型方法和泛型属性。泛型类和泛型接口统称为泛型类型。最重要的是泛型类型和泛型方法。

在Kotlin,类也可以有类型参数:

class Box<T>(t: T) {
    var value = t
}

一般来说,要创建如上这样类的实例,我们需要提供类型参数:

val box: Box<Int> = Box<Int>(1)

如果类型参数是可以推断出来的,也可以将其省略掉:

val box = Box(1)
  • 定义泛型类型,是在类型名之后、主构造函数之前用尖括号(<>)括起的大写字母类型参数指定。
  • 定义泛型类型变量,可以完整的写明类型参数;如果编译器可以自动推定类型参数,也可以省略类型参数。

在泛型方法的类型参数里可以用冒号指定上界。

fun <T : Comparable<T>> sort(list: List<T>) {/*...*/}

对于多个上界约束条件,可以用where子句:

fun <T> cloneWhenGreater(list: List<T>, threshold: T): List<T>
    where T : Comparable, Cloneable {
      return list.filter(it > threshold).map(it.clone())
    }

一、型变

在Kotlin中,没有通配符类型。但是它有:声明处型变和类型投影。

在Java中的泛型是不型变的。通配符类型参数 ? extends E表示此方法接受E或者E的一些子类型对象的集合,而不只是E自身。也就是说可以安全地从中读取E,但不能写入,因为我们不知道什么对象符合未知的E的子类型。带extends限定的通配符类型使得类型是协变的。

只能从中读取的对象为生产者,只能写入的对象为消费者。通配符保证的唯一的事情就是类型安全。

1.1声明处型变

如果有一个泛型接口Source<T>,这个接口中不存在任何以T为参数的方法,只是方法返回T类性值:

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

void demo(Source<String> str) {
  // Java 中这种写法是不允许的
  Source<Object> obj = str;
  /*...*/
}

因为Java中泛型是不型变的,Source<String> 不是Source<Object>的子类型,所以不能把Source<String>类型变量赋值给Source<Object>类型变量。

在Kotlin中,有一种方法向编译器解释这种情况。称为声明处型变:可以标注Source的类型参数T来确保它仅从Source<T>成员中返回,并从不被消费。Kotlin提供了out修饰符:

abstract class Source<out T> {
    abstract fun nextT(): T
}

fun demo(strs: Source<String>) {
    val objects: Source<Any> = strs // 这个没问题,因为 T 是一个 out-参数
    // ……
}
  • 当一个类C的类型参数T被声明为out时,就只能出现在C的成员的输出位置,但是C<Base>可以安全地作为C<Derived>的超类。

out修饰符称为型变注解,并且由于它在类型参数声明处提供,所以叫声明处型变

除了out,Kotlin又补充了一个型变注释:in。它使得一个类型参数逆变:只可以被消费而不可以被生产。逆变类的一个很好的例子是Comparable:

abstract class Comparable<in T> {
    abstract fun compareTo(other: T): Int
}

fun demo(x: Comparable<Number>) {
    x.compareTo(1.0) // 1.0 拥有类型 Double,它是 Number 的子类型
    // 因此,我们可以将 x 赋给类型为 Comparable <Double> 的变量
    val y: Comparable<Double> = x // OK!
}

1.2类型投影

使用处型变:类型投影
将类型参数T声明为out很方便,而且可以避免使用处子类型化的麻烦,但是有些类实际上不能限制为只返回T 。例如Array:

class Array<T>(val size: Int) {
    fun get(index: Int): T { ///* …… */ }
    fun set(index: Int, value: T) { ///* …… */ }
}

这个类在T上既不可以是型变,也不可以是逆变。从而造成了一些不灵活性。

fun copy(from: Array<Any>, to: Array<Any>) {
    assert(from.size == to.size)
    for (i in from.indices)
        to[i] = from[i]
}

要确保的是copy()不会做任何坏事。所以如下:

fun copy(from: Array<out Any>, to: Array<Any>) {
 // ……
}

这就发生了类型投影。这就是使用处型变的用法,并且是对应Java中的Array<? extends Object>,但是使用更简单。

也可以使用in投影一个类型:

fun fill(dest: Array<in String>, value: String) {
    // ……
}
1.2.1星投影

Kotlin为此提供了所谓的星投影语法:

  • 对于Foo<out T>,其中T是一个具有上界TUpper的协变类型参数,Foo<*>等价于Foo<out TUpper>。意味着当T未知时,可以安全地从Foo<*>读取TUpper的值。
  • 对于Foo<in T>,其中T是一个逆变类型参数,Foo<*>等价于Foo<in Nothing>。意味着当T未知时,没有什么可以以安全的方式写入Foo<*>。
  • 对于Foo<T>,其中T是一个具有上界TUpper的不型变类型参数,Foo<*>对于读取值时等价于Foo<out TUpper>而对于写值时等价于Foo<in Nothing>

如果泛型类型具有多个类型参数,则每个类型参数都可以单独投影。 例如,如果类型被声明为 interface Function <in T, out U>,我们可以想象以下星投影:

  • Function<*, String> 表示 Function<in Nothing, String>;
  • Function<Int, *> 表示 Function<Int, out Any?>;
  • Function<*, *> 表示 Function<in Nothing, out Any?>。

学海无涯苦作舟

我的微信公众号.jpg

相关文章

  • Kotlin学习之泛型

    Kotlin学习之泛型 Kotlin的泛型与Java一样,都是一种语法糖,只在源代码里出现,编译时会进行简单的字符...

  • Kotlin for android学习六:泛型

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

  • 泛型

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

  • Kotlin---泛型

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

  • Kotlin 泛型 VS Java 泛型

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

  • Kotlin 之 泛型

    参考: Kotlin 实战 Java 泛型推荐阅读:https://www.zhihu.com/question/...

  • 泛型

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

  • Kotlin 泛型

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

  • Kotlin:泛型杂谈(下)

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

  • 【Android】 Kotlin(七)泛型

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

网友评论

    本文标题:Kotlin学习之泛型

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