Kotlin基础——泛型

泛型类型参数

编译器一般可以推导出类型实参

在这里插入图片描述

若创建空的list,则需要显示指定类型实参,可以用如下两种方式

val name: MutableList<String> = mutableListOf()val name2 = mutableListOf<String>()

泛型函数

public fun <T> List<T>.slice(indices: IntRange): List<T> {if (indices.isEmpty()) return listOf()return this.subList(indices.start, indices.endInclusive + 1).toList()
}

如对于上面List的slice函数,在使用可不指定类型实参

val letters = ('a'..'z').toList()
println(letters.slice(0..2))
println(letters.slice<Char>(0..2))

泛型类

interface MyList<T> {operator fun get(index: Int): T
}

类型参数约束

如下指定了T为Number的子类,只能传递数字

fun <T : Number> onHalf(value: T): Double {return value.toDouble() / 2.0
}
println(onHalf(3))

如果需要多个约束,则使用where

fun <T> addSuffix(seq: T) where T : CharSequence, T : Appendable {if (!seq.endsWith(".")) {seq.append('.')}
}val s = StringBuilder("Hello")
addSuffix(s)
println(s)

非空类型形参

默认类型形参将使用Any?作为默认上界,即可以传递null作为参数

class Processor<T> {fun process(value: T) {value?.hashCode()}
}val p = Processor<String?>()
p.process(null)

若要保证类型形参不为空,需要显式使用Any为上界

class Processor<T : Any> {fun process(value: T) {value.hashCode()}
}

运行时的泛型

类型检查和转换

可以用is判断参数是否为List,但无法判断其类型参数,因为会泛型擦除,所以可以省略<>,或者使用<*>

fun printSum(c: Collection<Int>) {if (c is List<Int>) {println(c.sum())}
}
printSum(listOf(1, 2, 3))fun printSum2(c: Collection<Int>) {if (c is List<*>) {println(c.sum())}
}fun printSum3(c: Collection<Int>) {if (c is List) {println(c.sum())}
}

而在类型转换时,不能转换特定的类型参数,如下set<Int>转换为List<Int>会类型不匹配,而List<String>转换为List<Int>,实际是擦除后的List,但会在sum()调用时报错

fun printSum4(c: Collection<*>) {val intList = c as? List<Int> ?: throw IllegalStateException("list is expected")println(intList.sum())
}//printSum4(setOf(1, 2, 3))     //IllegalStateException:list is expected
//printSum4(listOf("1", "2", "3")) //java.lang.ClassCastException

声明带实化类型参数的函数(类型参数不被擦除)

如下,正常情况下不能判断类型参数的类型

在这里插入图片描述

但在内联函数中的类型参数可以被实化,使用时需要用reified标记类型参数

inline fun <reified T> isA(value: Any) = value is Tprintln(isA<String>("abc"))
println(isA<Int>(123))

库函数filterIsInstance可用于过滤指定类型参数的元素

val items = listOf("one", 2, "three")
println(items.filterIsInstance<String>())

Kotlin的inline函数可以被Java调用但不能被内联,但带reified的inline函数不能被Java调用(因为其必须内联)

使用实化类型参数代替类引用

如下,在使用需要参数为Class的方法时

var load = ServiceLoader.load(Service::class.java)	//等价于Java的Service.class

可以通过实化类型参数来重写

 inline fun <reified T> loadService(): ServiceLoader<T>? {return ServiceLoader.load(T::class.java)}var load = loadService<Service>()

实化类型参数的限制

优点:

  • 可以用于类型检查和转换(is、!is、as、as?)
  • 使用反射API(::class)
  • 获取Class (::class.java)
  • 作为调用其他函数的类型实参

缺点:

  • 不能创建类型参数的类的实例
  • 不能调用类型参数类的伴生对象的方法
  • 调用带实化参数类型函数的时候不能使用非实化参数类型形参作为类型实参
  • 不能把类、属性或者非内联函数的类型参数标记为refied

变型:泛型和子类型化

类、类型和子类型

对于非泛型类,类的名词可以直接当作类型使用,每一个Kotlin类可以构造两种类型,且不可空类型为可空类型的子类型

val s1: String? = null
val s2: String = "A"
fun accept(s: String?) {
}
accept(s1)
accept(s2)

而对于泛型类,需要类型实参替换泛型类的类型形参,每一个泛型类可以构造无数的类型,且它们没有任何关系

val l1: MutableList<Any> = mutableListOf()
val l2: MutableList<Int> = mutableListOf()
fun accept(l: MutableList<Any>) {
}
accept(l1)
//accept(l2)

协变:保留子类型化关系

对于类型参数T,如果用作函数参数在in位置,如果用作返回值在out位置

在这里插入图片描述

在类型参数加上out表示协变,可以保留类型参数的子类关系

public interface List<out E> : Collection<E> {......
}

对于不可变的List,其没有set方法(即不会出现往List<String>存入Int的情况),意味着不会对元素进行改变

val l3: List<Any> = listOf()
val l4: List<Int> = listOf()
fun accept(l: List<Any>) {
}
accept(l3)
accept(l4)
  • 构造函数的参数既不在in位置,也不在out位置,即使声明为out也仍可使用
  • 构造函数参数使用var,会生成setter,不能使用out标记
  • out/in只适用于类外部可见(public、protect和internal)的Api

逆变:反转子类型化关系

对于String的排序方法,接收一个Comparator<String>()

val s = listOf("AB", "C", "DEF")
s.sortedWith(kotlin.Comparator<String>() { s1, s2 ->s1.length - s2.length
})
println(s)

但它也可以接受一个更普遍的比较器Comparator<Any>()

val s = listOf("AB", "C", "DEF")
s.sortedWith(kotlin.Comparator<Any>() { s1, s2 ->s1.hashCode() - s2.hashCode()
})
println(s)

因为Comparator的类型参数由in修饰,是逆变的

interface Comparator<in T> {fun compare(e1: T, e2: T): Int {}
}

同时存在协变和逆变

对于同时存在in和out的函数,可用(P) -> R表示

interface Funtion1<in P, out R> {fun invoke(p: P): R
}

如下将Animal作为Cat逆变传参,将返回值Int作为Number协变返回

open class Animal {open fun getIndex(): Int {return 0}
}
class Cat : Animal() {override fun getIndex(): Int {return 1}
}
fun getCatIndex(f: (Cat) -> Number) {
}
getCatIndex(Animal::getIndex)

使用点变型:在类型出现的地方指定变型

上面在定义类和接口使用in和out叫做声明点变型,而在调用函数传递参数时使用in和out叫做使用点变型,如在Java中的List<T>,在写函数时可用使用通配符限制 (? exends 和 ? super)

fun <T> MutableList<T>.copy(source: MutableList<T>, destination: MutableList<T>) {for (item in source) {destination.add(item)}
}val intItem = mutableListOf(1, 2, 3)
val anyItem = mutableListOf<Any>()
//copy(intItem, anyItem)

对于上面的copy函数,source只读,destination只写,但实际不能把MutableList<String>拷贝到MutableList<Any>,要实现这个功能,需要引入第二个类型参数,修改为

fun <T : R, R> copy(source: MutableList<T>, destination: MutableList<R>) {for (item in source) {destination.add(item)}
}val intItem = mutableListOf(1, 2, 3)
val anyItem = mutableListOf<Any>()
copy(intItem, anyItem)

更为简单的方式是使用out,让其在使用点变型,对于只读的source可使用子对象

fun <T> copy(source: MutableList<out T>, destination: MutableList<T>) {for (item in source) {destination.add(item)}
}
val intItem = mutableListOf(1, 2, 3)
val anyItem = mutableListOf<Any>()
copy(intItem, anyItem)

使用*代替类型参数

  • MutableList<*>包含某种特定类型元素的列表,但不知道是哪个类型
  • MutableList<Any?>包含任意类型的元素的列表

不能创建MutableList<*>且不可写,但可读,因为所有类型可转为Any?

当类型实参的信息并不重要时,使用星号投影

fun printFirst(list: List<*>) {if (list.isNotEmpty()) {println(list.first())}
}

上面代码也可以通过引入类型参数替换

fun <T> printFirst(list: List<T>) {if (list.isNotEmpty()) {println(list.first())}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/486994.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

后端程序员入门react笔记(四)-综合运用,写一个小demo

样式模块化 有时候我们会遇到这样的问题&#xff0c;有两个css对一个class声明了样式&#xff0c;这样的话后引入的css会覆盖前面的css样式&#xff0c;导致样式冲突&#xff0c;那么我们怎么解决这种问题呢&#xff0c;我们可以使用样式的模块化&#xff0c;我们起名一个inde…

多线程、分布式运行用例

python多线程 threading模块 多线程实例 # -*- coding: utf-8 -*- # Time : 2024/2/7 15:50 # Author : 居里夫人吃橘子 # File : class01.py # Software: PyCharm import threading from time import sleepdef run(name):print(name 该起床了)sleep(2)print(name …

OpenAI文生视频大模型Sora概述

Sora&#xff0c;美国人工智能研究公司OpenAI发布的人工智能文生视频大模型&#xff08;但OpenAI并未单纯将其视为视频模型&#xff0c;而是作为“世界模拟器” &#xff09;&#xff0c;于2024年2月15日&#xff08;美国当地时间&#xff09;正式对外发布。 Sora可以根据用户…

jenkins配置ssh的时候测试连接出现Algorithm negotiation fail

背景&#xff1a;当jenkins升级后&#xff0c;同时ssh插件也升级&#xff0c;测试ssh连接的时候 出现的问题&#xff1a; com.jcraft.jsch.JSchAlgoNegoFailException: Algorithm negotiation fail: algorithmName"server_host_key" jschProposal"ecdsa-sha2-n…

解决SpringAMQP工作队列模型程序报错:WARN 48068:Failed to declare queue: simple.queue

这里写目录标题 1.运行环境2.报错信息3.解决方案4.查看解决之后的效果 1.运行环境 使用docker运行了RabbitMQ的服务器&#xff1a; 在idea中导入springAMQP的jar包&#xff0c;分别编写了子模块生产者publisher&#xff0c;消费者consumer&#xff1a; 1.在publisher中运行测试…

git 使用总结

文章目录 git merge 和 git rebasegit mergegit rebase总结 git merge 和 git rebase git merge git merge 最终效果说明&#xff1a; 假设有一个仓库情况如下&#xff0c;现需要进行 merge&#xff1a; merge 操作流程&#xff1a; merge 的回退操作&#xff1a; git reba…

【区块链】智能交易模式下的数据安全流通模型

【区块链】智能交易模式下的数据安全流通模型 写在最前面**区块链智能交易模式概述****数据安全流通的挑战****数据安全流通模型的核心要素****实现数据安全流通的区块链技术****区块链智能交易模式下数据安全流通模型的设计原则****数据安全流通模型的应用案例分析****面临的挑…

burpsuite插件编写之如何隐秘的划水

老板以为你在认真挖洞,没想到你居然在摸鱼哈哈哈哈。 一、开发环境准备 idea、jdk、maven 安装好即可 二、开发知识准备 有空可以看看下面的示例和api 官方各种示例代码:Burp extensions - PortSwigger 官方API文档:Generated Documentation (Untitled) 在burp的扩展那…

《图解密码技术》总结

一&#xff1a;首先在此介绍一些密码的常识&#xff1a; 1>不要使用保密的密码算法 2>使用低强度的密码比不进行任何加密更加危险 3>任何密码总有一天都会被破译 4>密码只是信息安全的一部分 二&#xff1a;逐章总结&#xff1a; 第一章&#xff1a;环游密码世…

黄海北部泥质沉积体地层界面如何划分?

黄海沉积物中存在北黄海泥M1、海阳东泥M2、老黄河口泥M3、南黄海中部泥M4和南黄海东部泥M5等泥质沉积&#xff0c;见下图。其中北黄海泥M1、海阳东泥M2和南黄海中部泥M4在空间上是连在一起的沉积体&#xff0c;从庙岛群岛起沿山东半岛北部向东绕过山东半岛东端向南延伸&#xf…

基于JAVA的公司货物订单管理系统 开源项目

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 客户管理模块2.2 商品维护模块2.3 供应商管理模块2.4 订单管理模块 三、系统展示四、核心代码4.1 查询供应商信息4.2 新增商品信息4.3 查询客户信息4.4 新增订单信息4.5 添加跟进子订单 五、免责说明 一、摘要 1.1 项目…

华为OD机试真题-CPU算力分配-2023年OD统一考试(C卷)--Python--开源

题目&#xff1a; ** ** 考察内容&#xff1a; 排序(sort)求和&#xff08;sum) 数学转化 循环&#xff08;从小开始&#xff09;break 代码&#xff1a; """ 题目分析&#xff1a; a和b初始总算力不同 从A组中选出的算力尽可能小 交换以后&#xff0c;a和b的…