IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> 关于kotlin泛型中的协变和逆变的个人理解 -> 正文阅读

[移动开发]关于kotlin泛型中的协变和逆变的个人理解

2021.8.26
带有主观性,理性观看,客观评价

WARNING:本文章适合了解一点协变和逆变但又不完全懂的人看一看

泛型协变:如果定义了一个MyClass< T >的泛型类,其中A是B的子类型,同时MyClass< A >又是MyClass< B >的子类型,那么就可以称MyClass在T这个泛型上是协变

错误写法:

open class Dad()

class Son(): Dad()

class Generics<out T> {
    private var data: T?=null
    fun get(): T?{
        return data
    }
    fun set(t: T?){
        data = t
    }
}

fun function(generics: Generics<Dad>){
    val dad = Dad()
    generics.set(dad)
}

fun main(){
    val sonGenerics = Generics<Son>()
    function(sonGenerics)
}

正确写法:

open class Dad()

class Son(): Dad()

class Generics<out T> {
    private var data: T?=null
    fun get(): T?{
        return data
    }
}

fun function(generics: Generics<Dad>){
    val dad: Dad? = generics.get()
}

fun main(){
    val sonGenerics = Generics<Son>()
    function(sonGenerics)
}

这边我定义了一个Generics泛型类,一个Dad父类,一个Son子类,先来说说为什么这样写是对的。
我这时候已经声明了T是只读的,那么不存在出现在in位置上的情况。
根据定义A是B的子类型,MyClass< A >就是是MyClass< B >的子类型,即使function需要的是一个泛型是Dad的Generics类,但Generics< Son >是Generics< Dad >的子类,就可以将参数传递进去,get方法就可以使用了,这个协变是很好理解的,就和多态差不多的道理。

注:generics.get()得到的是一个Son实例,但是被一个Dad类的dad引用接受了,这是完全可行的,因为多态,父类引用指向子类

再来说说错误写法错在哪,在这时候,我在Generics类中添加了set方法,如果我在function参数列表中传入一个Generics,这时候再调用它的set方法传入一个Dad实例,那么会出现类型转换异常(泛型指定Son而实例为Dad,Dad无法向下转型),这种行为是不安全的。

------------------------------------------分割线------------------------------------------

泛型逆变:如果定义了一个MyClass< T >的泛型类,其中A是B的子类型,同时MyClass< B >又是MyClass< A >的子类型,那么我们就可以称MyClass在T上是逆变的

错误写法:

open class Dad()

class Son(): Dad()

class Generics<in T> {
    private var data: T?=null
    fun get(): T?{
        return data
    }
    fun set(t: T?){
        data = t
    }
}

fun function(generics: Generics<Son>){
    val value: Son? = generics.get()
}

fun main(){
    val dadGenerics = Generics<Dad>()
    function(dadGenerics)
}

正确写法:

open class Dad()

class Son(): Dad()

class Generics<in T> {
    private var data: T?=null
    fun set(t: T?){
        data = t
    }
}

fun function(generics: Generics<Son>){
    val son = Son()
    generics.set(son)
}

fun main(){
    val dadGenerics = Generics<Dad>()
    function(dadGenerics)
}

同样的我们先来看看为什么正确写法为什么正确,function指定的参数是Generics类型,那么这时传入一个Generics类型的参数,因为声明了in,T是只写的,所以根据定义A是B的子类型,MyClass< B >就是MyClass< A >的子类型,Generics传入function中,由于Generics是Generics的子类型,那么这个参数传入是正确的,function内部调用set方法,为传入的dadGenerics赋值,此时又可以联想到多态,在Generics中设置一个Son类型的实例,这是完全成立的,不会出现类型转换异常

那么错在哪?如果在function中调用get方法,那么由于实际传入的参数是Generics类型的,会获取到Dad实例,然而function中参数指定的是Generics类型,那么编译器就会认为get得到的就是一个Son类型的实例(我显式声明了),又因为Dad实例不能向下转型为Son实例,所以这样会导致类型转换异常。

至于为什么如此设计,暂时立个flag,以后会进行更新

由于作者没有先了解Java的协变和逆变直接就学了kotlin的协变和逆变,可能理解有偏差,请谅解

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-08-27 11:58:42  更:2021-08-27 11:58:48 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2025年2日历 -2025/2/25 4:10:20-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码