首页 > 其他分享 >Kotlin中的泛型:协变与逆变

Kotlin中的泛型:协变与逆变

时间:2023-07-07 14:34:11浏览次数:42  
标签:子类 逆变 Kotlin List 类型 协变 泛型

协变与逆变

现在假设存在类A和类B,以及泛型类LIst<A>和泛型类LIst<B>,则协变和逆变的定义如下:

  • 协变

    如果A是B的子类,且List<A>是List<B>的子类,那么可以说泛型List<T>是协变的

  • 逆变
    如果A是B的子类,且List<B>是List<A>的子类,那么可以说泛型List<T>是逆变的

Java中的泛型

Java中的泛型不是协变的,即List<String>并不是List<Object>的子类,这样就会给一些操作带来了不必要的麻烦,例如:

	List<Object> list = new ArrayList<>();
	List<String> strings = new ArrayList<>();
	list.addAll(strings)

因为List<String>不是List<Object>的子类,理论上我们调用 addAll 方法是会编译报错的。但实际上并不会,原因是Java使用通配符类型来实现了协变。

查看Collections<E>类的源码可以发现 addAll 方法的参数类型实际上是 Collection<? extends E>,即对于所有E的子类型其对应的泛型类都是Collection<? extends E>的子类型。
上述示例中,因为Collection<String>是Collection<? extends Object>的子类,所以可以成功调用addAll方法。

同理,Java中也可以使用通配符类型实现逆变,对于所有E的父类型其对应的泛型类都是Collection<? super E>的子类型。

Kotlin中的泛型

Kotlin泛型的声明式协变和逆变

假设对于List<E>泛型类,仅包含输出类型E的接口,那么我们实际上输出任何E的子类型的实例都是安全的。例如:

	interface List<E> {
		fun get(index: Int) : E
	}

那么List<String>就是List<Object>的子类型,即可以将List<String>类型的对象赋值给List<Object>类型的变量,因为赋值给List<Object>类型的变量后实际只会输出String类型的实例,而String类型是Object类型的子类,所以是安全的。

上述接口在Kotlin中需要使用 out 关键字来表示该泛型是协变的,如下所示:

	interface List<out E> {
		fun get(index: Int) : E
	}

out 关键字即表示该泛型类型仅仅包含输出类型E的接口。

同理,假设List<E>泛型仅包含输入类型E的接口,那么实际上输入任何类型E的子类型的实例都是安全的,Kotlin中使用 in 关键字来表示该泛型是逆变的,如下所示:

	interface List<in E> {
		fun add(item: E)
	}

此种情况下,List<Object>就是List<String>的子类型,即可以将List<Object>类型的对象赋值给List<String>类型的变量,因为赋值给List<String>类型的变量后只能输入String类型的实例,而String类型是Object类型的子类,所以是安全的。

实际上来说,我们知道List接口不能只支持输入接口或者只支持输出接口,例如List接口需要同时支持get操作和add操作,那么List泛型就不能在类的声明处用in或者out关键字来声明协变或者逆变。对于此种情况,Kotlin中还支持使用处协变和逆变,即类型投影。

Kotlin泛型的类型投影

	interface List<E> {
		fun add(item: E)
		fun get(index: Int) : E
	}
	
	fun copy(list1: List<out Any>, list2: List<Any>) {
		//copy items from list1 to list2
	}

如上所示,copy 函数从 list1 中拷贝所有的元素到 list2 中,对于 list1 列表该函数只需要读取元素,因此可以在参数列表中使用 out 关键字声明 list1 是协变的,即在copy函数中对于 list1的操作只能是读取元素,而不能向 list1 写入任何元素。

同理,如果一个函数只能对某个参数进行写入操作,那么就可以在参数列表中使用 in 关键字来声明。例如:

	fun fill(list: List<in String>, str: String) {
		//fill list with str
	}

标签:子类,逆变,Kotlin,List,类型,协变,泛型
From: https://www.cnblogs.com/jqctop1/p/17481736.html

相关文章

  • Kotlin 常用语法糖记录
    原文地址:Kotlin常用语法糖记录-Stars-One的杂货小窝当使用Kotlin编程时,有一些常用的函数可以帮助我们简化代码并提高开发效率。稍微列举下常用的方法runCatchingrunCatching是一个用于处理可能引发异常的代码块的函数。它提供了一种更简洁和安全的方式来执行可能出现......
  • Kotlin协程:打破线程框架的思维
    Kotlin协程:打破线程框架的思维前言协程是Kotlin对比Java的最大优势,需要理解协程的设计理念和知识体系,建立协程思维模型。本文将介绍协程的概念、特性和原理,以及如何在Android开发中使用协程来简化并发编程和优化软件架构。什么是协程协程是一种互相协作的程序,可以在任意地方挂......
  • java 中协变,逆变,不变简单理解
    1.什么是协变、逆变、不变假设有两个类,Dog和Animal,如果用Dog<=Animal表示它俩的继承关系。用f(type)表示类型构造器,一个已知的类型被类型构造器处理后就是一个崭新的类型。协变就是f(Dog)是f(Animal)的子类,即f(Dog)<=f(Animal);逆变就是f(Animal)是f(Dog)的子类,即f(Ani......
  • Kotlin 集合 - 创建列表、集合和映射
    集合是保存多个相同或不同类型元素的容器。它们提供了各种方法和操作来有效地操纵和访问存储的数据。了解如何创建和使用集合对于任何Kotlin开发人员都至关重要,因为这使他们能够有效地组织和管理数据。列表列表是Kotlin中允许重复元素的有序集合。它们提供了根据元素在列表中的......
  • Android 构建脚本从Groovy迁移到Kotlin DSL
    原文:https://edenxio.github.io/2019/02/01/Android%20%E6%9E%84%E5%BB%BA%E8%84%9A%E6%9C%AC%E4%BB%8EGroovy%E8%BF%81%E7%A7%BB%E5%88%B0Kotlin%20DSL/ 为什么要迁移?因为Groovy是动态语言,在用作Android构建脚本的时候,经常有些问题:很差的IDE支持(自动提示等)性能问题......
  • Kotlin + buildSrc:更好的管理Gadle依赖
    为了充分利用AndroidPluginforGradle3.0+的优点,将Android项目拆分成多个module的做法越来越常见。然而,随着module数量的增多,我们很快就会遇到依赖管理的混乱问题。管理Gradle依赖的三种不同方法:手动管理使用Google推荐的“ext”Kotlin+buildSrc1、手动管理这是一种大多数人在......
  • 【Android Kotlin】全网最全的Android Kotlin入门教程指南,入股不亏
    前言2017年的谷歌大会上,Kotlin被指定为官方的Android开发语言,从长远的角度来看,Kotlin语言是作为Android开发者必须要掌握的一门编程语言。Kotlin编程语言极易上手,而且它不像Java那样代码繁琐,Kotlin代码更简洁,可读性也更强,可以花更少的时间来编写和理解代码,极大地提高了工作效率,不仅......
  • # 泛型 \<T>
    泛型是在C++中就已经存在的功能,而C#也自然继承了这一个非常重要的功能。泛型可以应用于完全一致类型的数据交流,但由于不能事先知道数据类型,因此只能做一些访问和相互赋值功能。例如如下代码voidswap<T>(refTa,refTb){Ttemp=a;a=b;b=temp;}//St......
  • 为什么说Kotlin是未来
    相比起Java语言,Kotlin的优势确实非常明显:第一,极高的生产效率。Kotlin是一种跨平台的静态类型语言,具有现代简洁的语法,关键特性包括null安全性、协程、数据类型、扩展函数等;这让开发者会用得很爽:前期开发效率更高,中期线上问题更少,后期代码更容易维护。而这正是Java做不到的......
  • 浅谈 Kotlin 与 Java 互操作 (上)
    前言浅谈Kotlin与Java互操作(上)Kotlinis100%interoperablewithJavaandAndroidKotlin官网的一句标语,其旨意是表达kotlin的Interoperable-互操作特性互操作就表示Kotlin中可以调用Java的开放接口来访问成员属性和成员方法,同时在Java代码中也百分百兼容Kotlin......