美文网首页Android开发
快速掌握Kotlin泛型:不变、协变、逆变

快速掌握Kotlin泛型:不变、协变、逆变

作者: 青叶小小 | 来源:发表于2021-02-21 00:28 被阅读0次

一、前言

我们在讲 Java 的《泛型: super,extend,?》一文中,在最后有提到PECS,并有给出一个结论:

\color{red}{Java 的上下界和 Kotlin 中的协变逆变是等同的!}

那本章还要讲啥呢?

本章讲的是:类型转换(即 赋值)


Kotlin 中泛型引入了 in 和 out:

  • 逆变(In)

如果你的类是将泛型对象作为函数的参数,那么可以用 in:

 interface Consumer<in T> {
    fun consume(item: T)
}

可以称其为 consumer class/interface,因为其主要是消费指定泛型对象。

  • 协变(Out)

如果你的类是将泛型作为内部方法的返回,那么可以用 out:

interface Production<out T> {
    fun produce(): T
}

可以称其为 production class/interface,因为其主要是产生(produce)指定泛型对象。

  • 不变(Invariant)

如果既将泛型作为函数参数,又将泛型作为函数的输出,那就既不用 in 或 out。

interface ProductionConsumer<T> {
    fun produce(): T
    fun consume(item: T)
}

二、协变(Out)

我们先来举个例子:红苹果! 它既是苹果,又是水果!

fruit-out.png

代码实现如下:

interface Production<out T> {
    fun produce(): T
}

open class Fruit {}
open class Apple: Fruit() {}
class RedApple: Apple() {}

class FruitFactory : Production<Fruit> {
    override fun produce(): Fruit {
        return Fruit()
    }
}

class AppleFactory : Production<Apple> {
    override fun produce(): Apple {
        return Apple()
    }
}

class RedAppleFactory : Production<RedApple> {
    override fun produce(): RedApple {
        return RedApple()
    }
}

// below is OK !
// 子类泛型的对象赋值给使用父类泛型的对象
val factory0 : Production<Fruit> = FruitFactory()
val factory1 : Production<Fruit> = AppleFactory()
val factory2 : Production<Fruit> = RedAppleFactory()

// 父类泛型的对象赋值给使用子类泛型的对象 isn't OK !
val factory00 : Production<RedApple> = FruitFactory()     // Error: Type mismatch
val factory01 : Production<RedApple> = AppleFactory()     // Error: Type mismatch
val factory02 : Production<RedApple> = RedAppleFactory()

很显然,红苹果工厂属于苹果工厂,也属于水果工厂。

因此,对于 out 泛型,我们能够将使用子类泛型的对象赋值给使用父类泛型的对象。

三、逆变(In)

再根据上面的例子,我们来创建消费者模型:

interface Consumer<in T> {
    fun consume(data: T)
}

open class Fruit {}
open class Apple: Fruit() {}
class RedApple: Apple() {}

class People : Consumer<Fruit> {
    override fun consume(data: Fruit) {
        println("People consume Fruit")
    }
}

class Man : Consumer<Apple> {
    override fun consume(data: Apple) {
        println("People consume Apple")
    }
}

class Child : Consumer<RedApple> {
    override fun consume(data: RedApple) {
        println("People consume RedApple")
    }
}

val person0 : Consumer<Apple> = People()
val person1 : Consumer<Apple> = Man()
val person2 : Consumer<Apple> = Child() // Error

很显然,这里的『苹果』消费者只能是『People』和『Man』。

因此,对于 in 泛型,我们能够将使用父类泛型的对象赋值给使用子类泛型的对象。

四、总结

根据以上的内容,我们还可以这样来理解什么时候用 in 和 out:

  • 父类泛型对象可以赋值给子类泛型对象,用 in;
  • 子类泛型对象可以赋值给父类泛型对象,用 out;
in-out-relation.png

相关文章

  • Scala 通俗易懂 ---- 协变、逆变、不变

    协变、逆变、不变 Scala 语言中协变、逆变、不变是指拥有泛型的类型,在声明和赋值时的对应关系 协变:声明时泛型...

  • 快速掌握Kotlin泛型:不变、协变、逆变

    一、前言 我们在讲 Java 的《泛型: super,extend,?》[https://www.jianshu....

  • Kotlin学习笔记 - 泛型

    1. 基本用法 2. 型变 型变包括 协变、逆变、不变 三种: 协变:泛型类型与实参的继承关系相同 逆变:泛型类型...

  • Scala 泛型协变与泛型边界

    代码准备 泛型协变 泛型协变、逆变、不变是指拥有泛型的类在声明和赋值时的对应关系。 协变:声明时泛型是父类,赋值时...

  • Scala 泛型与补充知识

    一、泛型 1.1不变、协变、逆变 语法 说明协变:Son 是 Father 的子类,则 MyList[Son] 也...

  • JAVA泛型与类型安全

    1. 基础泛型 2. 协变与逆变与不变 协变 简单来说即: Java中的数组是协变的 逆变与协变相对,逆转了类型关...

  • Kotlin学习03

    上篇回顾 在上篇中我们学习了Java,Kotlin的协变和逆变,知道了泛型是不变的和pecs口诀 任务 包 可见度...

  • 泛型编程中的型变

    在泛型编程中,经常会提到型变。型变分为两种:协变与逆变。协变covariant表示与泛型参数T的变化相同,而逆变c...

  • Kotlin 泛型:协变、逆变

    1、Why?为什么需要泛型? 根本目的是在保证泛型类 类型安全的基础上,提高API的灵活性 2、How?如何保证类...

  • Kotlin泛型与协变及逆变剖析

    Kotlin泛型与协变及逆变剖析 关于泛型的使用其实很简单,但是!!如文章开头所说,一直理解不了在Java框架中很...

网友评论

    本文标题:快速掌握Kotlin泛型:不变、协变、逆变

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