理解Kotlin泛型
创始人
2024-05-03 19:52:02
0

文章目录

      • Kotlin泛型
        • 声明处型变
          • 协变< out T>
          • 逆变< in T>
        • 使用处型变(类型投影)
      • 参考

Kotlin泛型

声明处型变

协变< out T>
interface GenericsP {fun get(): T  //读取并返回T,可以认为只能读取T的对象是生产者
}

如上声明了GenericsP< T>接口,如果其内部只能读取并返回T,可以认为GenericsP实例对象为生产者(返回T)。继续声明如下实体类:

open class Book(val name: String)
data class EnglishBook(val english: String) : Book(english)
data class MathBook(val math: String) : Book(math)

已知EnglishBook、MathBook为Book的子类,通常可以像下面这样声明:

val book: Book = EnglishBook("英语")

一般在实现多态时会这么写,但是如果将Book、EnglishBook当成泛型放入GenericsP,他们之间的关系还成立吗?即:

error信息
可以看到编译器直接报错,因为虽然EnglishBook是Book的子类,但是GenericsP< EnglishBook>并不是GenericsP< Book>的子类,如果想让这个关系也成立,Kotlin提供了out修饰符,out修饰符能够确保:

1、T只能用于函数返回中,不能用于参数输入中;
2、GenericsP< EnglishBook>可以安全的作为GenericsP< Book>的子类

示例如下:

interface GenericsP {fun get(): T  //读取并返回T,可以认为只能读取T的对象是生产者// fun put(item: T) //错误,不允许在输入参数中使用
}

经过如上的改动后,可以看到GenericsP< EnglishBook>可以正确赋值给GenericsP< Book>了:
正确赋值

逆变< in T>
interface GenericsC {fun put(item: T) //写入T,可以认为只能写入T的对象是消费者
}

如上声明了GenericsC< T>接口,如果其内部只能写入T,可以认为GenericsC实例对象为消费者(消费T)。为了保证T只能出现在参数输入位置,而不能出现在函数返回位置上,Kotlin可以使用in进行控制:

interface GenericsC {fun put(item: T) //写入T,可以认为只能写入T的对象是消费者//fun get(): T  //错误,不允许在返回中使用
}

继续声明如下函数:

    /*** 称为GenericsC在Book上是逆变的。* 跟系统源码中的Comparable类似*/private fun consume(to: GenericsC) {//GenericsC实例赋值给了GenericsCval target: GenericsC = totarget.put(EnglishBook("英语"))}

可以看到GenericsC中的泛型参数声明为in后,GenericsC实例可以直接赋值给了GenericsC< EnglishBook>,称为GenericsC在Book上是逆变的。在系统源码中我们经常使用的一个例子就是Comparable:

//Comparable.kt
public interface Comparable {public operator fun compareTo(other: T): Int
}

使用处型变(类型投影)

上一节中in、out都是写在类的声明处,从而控制泛型参数的使用场景,但是如果泛型参数既可能出现在函数入参中,又可能出现在函数返回中,典型的类就是Array:

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

这时候就不能在声明处做任何协变/逆变的操作了,如下函数中使用Array:

    fun copy(from: Array, to: Array) {if (from.size != to.size) returnfor (i in from.indices)to[i] = from[i]}

调用方:

val strs: Array = arrayOf("1", "2")
val any = Array(2) {}
copy(strs, any) //编译器报错 strs其类型为 Array 但此处期望 Array

参考

【1】https://www.kotlincn.net/docs/reference/generics.html
【2】https://mp.weixin.qq.com/s/vSwx7fgROJcrQwEOW7Ws8A
【3】https://juejin.cn/post/7042606952311947278

相关内容

热门资讯

【NI Multisim 14...   目录 序言 一、工具栏 🍊1.“标准”工具栏 🍊 2.视图工具...
银河麒麟V10SP1高级服务器... 银河麒麟高级服务器操作系统简介: 银河麒麟高级服务器操作系统V10是针对企业级关键业务...
不能访问光猫的的管理页面 光猫是现代家庭宽带网络的重要组成部分,它可以提供高速稳定的网络连接。但是,有时候我们会遇到不能访问光...
AWSECS:访问外部网络时出... 如果您在AWS ECS中部署了应用程序,并且该应用程序需要访问外部网络,但是无法正常访问,可能是因为...
Android|无法访问或保存... 这个问题可能是由于权限设置不正确导致的。您需要在应用程序清单文件中添加以下代码来请求适当的权限:此外...
AWSElasticBeans... 在Dockerfile中手动配置nginx反向代理。例如,在Dockerfile中添加以下代码:FR...
北信源内网安全管理卸载 北信源内网安全管理是一款网络安全管理软件,主要用于保护内网安全。在日常使用过程中,卸载该软件是一种常...
AsusVivobook无法开... 首先,我们可以尝试重置BIOS(Basic Input/Output System)来解决这个问题。...
月入8000+的steam搬砖... 大家好,我是阿阳 今天要给大家介绍的是 steam 游戏搬砖项目,目前...
​ToDesk 远程工具安装及... 目录 前言 ToDesk 优势 ToDesk 下载安装 ToDesk 功能展示 文件传输 设备链接 ...