美文网首页
Kotlin泛型与协变及逆变剖析

Kotlin泛型与协变及逆变剖析

作者: 蒋斌文 | 来源:发表于2021-06-25 18:43 被阅读0次

Kotlin泛型与协变及逆变剖析

关于泛型的使用其实很简单,但是!!如文章开头所说,一直理解不了在Java框架中很多使用泛型都会用<? extends T>和<? super T>

抛开语言层面来对泛型这块的东东彻底把它搞清楚,也就是关于泛型的协变与逆变的概念,下面先从Java的具体泛型的示例中来开始一点点理解它

public class Test {

    List<String> list = new ArrayList<>();
    List<Object> list2 = new ArrayList<>();
    
}

但是!!!我们不能这样做:

image-20210625175803891

这是为啥呢?假设这个等式成立的话,那可以这样写:

    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        List<Object> list2 = list;
        list2.add(new Date());
    }

由于list2指向了list,那么我可以以list的角度来取数据,那么就会有:

image-20210625180154398

为了解决这样的问题,Java提供了通配符,如下:

public class Test {


    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
//        List<Object> list2 = new ArrayList<>();
        List<? extends  Object> list2 = list;
    }

}
image-20210625180248064

下面再以集合为例对其进行说明:

image-20210625180401582

集合接口中定义了一个addAll()方法,可以添加现已经存在的集合, 下面定义一个添加方法:

image-20210625180442923

很显然此添加方法在实际需求中是很常见的,这里就可以瞅一下JDK自带的Collection.addAll()方法的定义:

boolean addAll(Collection<? extends E> c);
image-20210625180643134

所以咱们校仿一下:

image-20210625180837367

这样的话,我们就可以很安全的当E类型从集合中去读取,也就是会当Object的类型来读取,所以Collection<String>是Collection<? extends Object>的子类型,这种情况就叫做协变,它只会从集合中当成Object来读取元素,协变是针对读取的;

Collection<String>就是Collection<? extends Object>的子类型

相反如果这样写的话则是逆变,逆变是针对写入的,如下:

image-20210625181320468

也就是往集合中写元素时必须是String以上的类型,另外需要明白:我们如果只从中读取数据,而不往里面写入内容,那么这样的对象叫做生产者,也就是协变,此时使用 ? extends E;如果只向里面写入数据,而不从中读取数据,那么这样的对象叫做消费者,也就是逆变,使用? super E。

Kotlin中如何来解决协变和逆变的问题

fun main(args: Array<String>) {

    var myClass = MyClass<String>("abc")
    myTest(myClass)
}

class MyClass< T>(t: T) {

    private var t: T

    init {
        this.t = t
    }

    fun get(): T = this.t

}

fun myTest(myClass: MyClass<String>) {
    var myObject: MyClass<String> = myClass

    println(myObject.get())
}

RUN> 🏄🏄🏄🏄🏄🏄

abc

接下来要改动程序:

image-20210625183113082

此时可以看到我们定义的泛型,在整个类中只有读取,没有写入,如下:

class MyClass< T>(t: T) {

    private var t: T

    init {
        this.t = t
    }
    fun get(): T = this.t
}

这其实是协变,这时如果在Java中就可以声明为? extents T,但是在Koltin中用out它来表示协变:

image-20210625183407082

表示该泛型是要被读的,那如果给T增加一个写方法呢?

image-20210625183620377

所以可见对于协变只能用到读,接下来定义一个逆变:

class MyClass<out T, in M>(t: T, m: M) {

    private var t: T

    private var m: M

    init {
        this.t = t
        this.m = m
    }

    fun get(): T = this.t //协变(covariant)只从中读取数据,而不往里面写入内容,那么这样的对象叫做生产者,也就是协变

    fun set(m: M) { //逆变(controvariant)只向里面写入数据,而不从中读取数据,那么这样的对象叫做消费者,也就是逆变
        this.m = m
    }
}

fun myTest(myClass: MyClass<String, Number>) {
    var myObject: MyClass<Any, Int> = myClass

    println(myObject.get())
}
image-20210625183907739

相关文章

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

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

  • Kotlin学习笔记 - 泛型

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

  • 泛型编程中的型变

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

  • Java协变和逆变

    泛型的协变与逆变 协变与逆变用来描述类型转换(type transformation)后的继承关系,其定义如下:如...

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

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

  • JAVA泛型与类型安全

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

  • Scala 泛型协变与泛型边界

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

  • Kotlin 泛型:协变、逆变

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

  • Java 泛型与通配符

    参考地址:《Java 泛型,你了解类型擦除吗?》 《Java中的逆变与协变》 《java 泛型中 T、E .....

  • 12.Kotlin泛型与协变及逆变原理剖析

    1.密封类 密封类(sealed class)①密封类用来表示受限的类继承结构,对密封类中的某个值来说,它所属类型...

网友评论

      本文标题:Kotlin泛型与协变及逆变剖析

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