当前位置: 首页 > news >正文

20:kotlin 类和对象 --泛型(Generics)

类可以有类型参数

class Box<T>(t: T) {var value = t
}

要创建类实例,需提供类型参数

val box: Box<Int> = Box<Int>(1)

如果类型可以被推断出来,可以省略

val box = Box(1)

通配符

在JAVA泛型中有通配符?? extends E? super E,在kotlin中没有这个概念,取而代之的是Declaration-site variancetype projections

Declaration-site variance

out 协变

对于如下代码

interface Source<T> {fun next():T
}fun demo(x : Source<Number>){val objects: Source<Any> = x
}

编译器报错 – 类型不匹配
在这里插入图片描述

想要代码成立需要在泛型定义时使用out

interface Source<T> {fun next():T
}fun demo(x : Source<Number>){val objects: Source<Any> = x
}

in 逆变

对于代码

interface Comparable<T> {operator fun compareTo(other: T): Int
}fun demo(x: Comparable<Number>) {x.compareTo(1.0)val y: Comparable<Int> = x
}

报错类型不匹配
在这里插入图片描述
想要代码成立需要在泛型定义时使用in

interface Comparable<in T> {operator fun compareTo(other: T): Int
}fun demo(x: Comparable<Number>) {x.compareTo(1.0)val y: Comparable<Int> = x
}

如果T类型作为参数(消费)就是用in,如果作为返回值(生产)就用out
Consumer in, Producer out!
逆变就是大类型变小类型,协变就是小类型变大类型

type projections(类型投影)

Use-site variance: type projections(使用点位变异:类型投影)

对于以下代码

class Demo<T>{fun copy(from: Array<T>, to: Array<T>) {assert(from.size == from.size)for (i in from.indices) {to[i] = from.get(i)}}
}fun main() {val str: Array<String> = arrayOf("hello", "world")val obj: Array<Any> = arrayOf(123, 432)Demo<Any>().copy(str, obj)
}

报错
在这里插入图片描述
在类声明时,不管是使用class Demo<in T>还是class Demo<out T>都会报错,为解决这种情况,可以修改copy方法的from参数类型为from: Array<out T>,因为from作为生产者,生产数组中的值

class Demo<T>{fun copy(from: Array<out T>, to: Array<T>) {assert(from.size == from.size)for (i in from.indices) {to[i] = from.get(i)}}
}

当然也可以改成下边写法

class Demo<T>{fun copy(from: Array<T>, to: Array<in T>) {assert(from.size == from.size)for (i in from.indices) {to[i] = from.get(i)}}
}fun main() {val str: Array<String> = arrayOf("hello", "world")val obj: Array<Any> = arrayOf(123, 432)Demo<String>().copy(str, obj)
}

星号投影(*)

有时候参数是一个泛型类型,但是在定义方法的时候不能确定泛型的具体类型,需要用到*投影
语法如下

  • 对于泛型类型Foo<out T : TUpper>T是一个具有上界TUpper的协变类型参数,Foo<*>等价于Foo<out TUpper>。这意味着当T未知时,你可以安全地从Foo<*>中读取TUpper的值。
  • 对于泛型类型Foo<in T>T是一个逆变类型参数,Foo<*>等价于Foo<in Nothing>。这意味着当T未知时,你无法以安全的方式向Foo<*>写入任何值。
  • 对于泛型类型Foo<T : TUpper>T是一个不变类型参数,具有上界TUpperFoo<*>在读取值时等价于Foo<out TUpper>,在写入值时等价于Foo<in Nothing>

举个例子

class Box<out T : Any>(private val value: T) {fun getValue(): T {return value}
}fun printBoxValue(box: Box<*>) {val value = box.getValue()println(value)
}fun main(){printBoxValue(Box(123)) // 123printBoxValue(Box("hello world"))   // hello world
}

printBoxValue方法的参数使用*投影

如果一个泛型类型有多个类型参数,每个参数可以独立进行投影

泛型函数

不仅类可以有类型参数,函数也可以有类型参数。类型参数位于函数名称之前

fun <T> singletonList(item: T): List<T> {// ...
}fun <T> T.basicToString(): String { // 扩展函数// ...
}

要调用泛型函数,在调用点的函数名称之后指定类型参数

val l = singletonList<Int>(1)

如果可以从上下文中推断出类型参数,则可以省略类型参数

val l = singletonList(1)

泛型约束

对于给定的类型参数,可以通过泛型约束来限制可替代的所有可能类型。

最常见的约束类型是上界(upper bounds)

fun <T : Comparable<T>> sort(list: List<T>) {  ... }

在冒号后指定的类型是上界,表示只有Comparable<T>的子类型可以替代T

sort(listOf(1, 2, 3)) // 正确。Int是Comparable<Int>的子类型
sort(listOf(HashMap<Int, String>())) // 错误:HashMap<Int, String>不是Comparable<HashMap<Int, String>>的子类型

如果不指定上界,则默认为Any?类型的

当一个类型参数需要满足多个上界时,需要使用 where 子句来指定这些上界条件

fun <T> processValues(list: List<T>) where T : CharSequence, T : Comparable<T> {val sortedValues = list.sorted()for (value in sortedValues) {println(value)}
}val stringList: List<String> = listOf("apple", "banana", "cherry")
val intList: List<Int> = listOf(1, 2, 3)
val mixedList: List<Any> = listOf("hello", 42, true)processValues(stringList) // apple, banana, cherry
processValues(intList) // 报错 -- Int 不满足 CharSequence 的上界
processValues(mixedList) // 报错 --Any 不满足 CharSequence 的上界

绝对非空类型(Definitely non-nullable types)

为了方便和java接口和类交互,如果有如下java接口

import org.jetbrains.annotations.*;public interface Game<T> {public T save(T x) {}@NotNullpublic T load(@NotNull T x) {}
}

要继承该接口并重写load方法,使用& Any来声明一个非空参数

interface ArcadeGame<T1> : Game<T1> {override fun save(x: T1): T1// T1 is definitely non-nullableoverride fun load(x: T1 & Any): T1 & Any
}

如果是纯kotlin项目,不需要使用此方法声明,kotlin的类型推断会做这件事

class ArcadeGame<T> {fun load(x: T & Any){println(x)}
}fun main(){ArcadeGame<String?>().load(null)    // 这里String?即使可以为空,调用load方法时传入null依旧报错
}

类型擦除

kotlin对泛型声明的类型安全检查是在编译时进行的。在运行时,泛型类型的实例不保存有关其实际类型参数的任何信息。这种类型信息被称为擦除。例如,Foo<Bar>Foo<Baz?> 的实例在擦除后变为 Foo<*>

泛型类型的检查和转换

由于类型擦除的存在,不能使用is进行如下检查

class ArcadeGame<T>{fun check(x:Any){if (x is T){}   // 报错 -- Cannot check for instance of erased type: T}
}fun main() {val game = ArcadeGame<String?>()if (game is ArcadeGame<String>) {} // 报错 -- Cannot check for instance of erased type: ArcadeGame<String>
}

可以使用型号投影进行检查

class ArcadeGame<T>fun main() {val game = ArcadeGame<String?>()if (game is ArcadeGame<*>) {} 
}

对于x is T这种检查方式,可以进行如下改造

class ArcadeGame<T>(private val type: Class<T>) {fun check(x: Any) {if (type.isInstance(x)) {// x 是 T 类型的实例}}
}fun main() {val game = ArcadeGame<String>(String::class.java)
}

The type arguments of generic function calls are also only checked at compile time. Inside the function bodies, the type parameters cannot be used for type checks, and type casts to type parameters (foo as T) are unchecked. The only exclusion is inline functions with reified type parameters, which have their actual type arguments inlined at each call site. This enables type checks and casts for the type parameters. However, the restrictions described above still apply for instances of generic types used inside checks or casts. For example, in the type check arg is T, if arg is an instance of a generic type itself, its type arguments are still erased.

inline fun <reified A, reified B> Pair<*, *>.asPairOf(): Pair<A, B>? {if (first !is A || second !is B) return nullreturn first as A to second as B
}val somePair: Pair<Any?, Any?> = "items" to listOf(1, 2, 3)val stringToSomething = somePair.asPairOf<String, Any>()
val stringToInt = somePair.asPairOf<String, Int>()
val stringToList = somePair.asPairOf<String, List<*>>()
val stringToStringList = somePair.asPairOf<String, List<String>>() // Compiles but breaks type safety!
// Expand the sample for more details

未经检查的类型转换(Unchecked casts)

对于泛型类型转换,无法在运行时进行检查。

fun gen(): Map<String, *> {return mapOf("one" to "你好", "two" to 123)
}fun main() {val gen = gen()gen as Map<Int, Int>	// 提示 -- Unchecked cast: Map<String, *> to Map<Int, Int>println(gen)	// {one=你好, two=123}
}

因为类型擦除的缘故,gen as Map<Int, Int>并不会报错,只是在编译期做出提醒

如果是这样转换则会报错

fun main() {val gen = gen()gen["one"] as Int   // 报错 -- java.lang.ClassCastException: class java.lang.String cannot be cast to class java.lang.Integer (java.lang.String and java.lang.Integer are in module java.base of loader 'bootstrap')println(gen)
}

如果不想提示,使用注解@Suppress("UNCHECKED_CAST")

fun main() {val gen = gen()@Suppress("UNCHECKED_CAST")gen as Map<Int, Int>println(gen)
}

JVM上,数组类型保留有关其元素被擦除的类型的信息,并且对数组类型的类型转换进行了部分检查:元素类型的可为空性和实际类型参数仍然被擦除。

fun gen(): Array<*> {return arrayOf("hello", "world")
}fun main() {val gen = gen()gen as Array<Int>   //  java.lang.ClassCastException: class [Ljava.lang.String; cannot be cast to class [Ljava.lang.Integer; ([Ljava.lang.String; and [Ljava.lang.Integer; are in module java.base of loader 'bootstrap')println(gen)
}

类型参数的下划线操作符

当其他类型被显式指定时,可以使用下划线操作符来自动推断参数的类型

abstract class SomeClass<T> {abstract fun execute() : T
}class SomeImplementation : SomeClass<String>() {override fun execute(): String = "Test"
}class OtherImplementation : SomeClass<Int>() {override fun execute(): Int = 42
}object Runner {inline fun <reified S: SomeClass<T>, T> run() : T {return S::class.java.getDeclaredConstructor().newInstance().execute()}
}fun main() {// T 是 String 类型,因为SomeImplementation为SomeClass<String>val s = Runner.run<SomeImplementation, _>()assert(s == "Test")// T 是 Int 类型 ,因为SomeImplementation为SomeClass<Int>val n = Runner.run<OtherImplementation, _>()assert(n == 42)
}

相关文章:

20:kotlin 类和对象 --泛型(Generics)

类可以有类型参数 class Box<T>(t: T) {var value t }要创建类实例&#xff0c;需提供类型参数 val box: Box<Int> Box<Int>(1)如果类型可以被推断出来&#xff0c;可以省略 val box Box(1)通配符 在JAVA泛型中有通配符?、? extends E、? super E&…...

我对迁移学习的一点理解(系列2)

文章目录 我对迁移学习的一点理解 我对迁移学习的一点理解 源域和目标域是相对的概念&#xff0c;指的是在迁移学习任务中涉及到的两个不同的数据集或领域。 源域&#xff08;Source Domain&#xff09;通常指的是已经进行过训练和学习的数据集&#xff0c;它被用来提取特征、…...

Spring MVC学习随笔-控制器(Controller)开发详解:控制器跳转与作用域(二)视图模板、静态资源访问

学习视频&#xff1a;孙哥说SpringMVC&#xff1a;结合Thymeleaf&#xff0c;重塑你的MVC世界&#xff01;&#xff5c;前所未有的Web开发探索之旅 衔接上文Spring MVC学习随笔-控制器(Controller)开发详解&#xff1a;控制器跳转与作用域&#xff08;一&#xff09; SpingMVC中…...

原型模式(Prototype Pattern)

1 基本概念 1.1 大佬文章 设计模式是什么鬼&#xff08;原型&#xff09; 详解设计模式&#xff1a;原型模式-腾讯云开发者社区-腾讯云 1.2 知识汇总 &#xff08;1&#xff09;原型模式&#xff1a;先 new 一个实例&#xff0c;该实例符合需求&#xff0c;之后再根据这个实…...

IM通信技术快速入门:短轮询、长轮询、SSE、WebSocket

文章目录 1. 引言2. 短轮询&#xff08;Short Polling&#xff09;2.1 原理2.2 代码示例2.2.1 服务器端&#xff08;Node.js&#xff09;2.2.2 客户端&#xff08;HTML JavaScript&#xff09; 3. 长轮询&#xff08;Long Polling&#xff09;3.1 原理3.2 代码示例3.2.1 服务器…...

04_W5500_TCP_Server

上一节我们完成了TCP_Client实验&#xff0c;这节使用W5500作为服务端与TCP客户端进行通信。 目录 1.W5500服务端要做的&#xff1a; 2.代码分析&#xff1a; 3.测试&#xff1a; 1.W5500服务端要做的&#xff1a; 服务端只需要打开socket&#xff0c;然后监听端口即可。 2…...

入门Redis学习总结

记录之前刚学习Redis 的笔记&#xff0c; 主要包括Redis的基本数据结构、Redis 发布订阅机制、Redis 事务、Redis 服务器相关及采用Spring Boot 集成Redis 实现增删改查基本功能 一&#xff1a;常用命令及数据结构 1.Redis 键(key) # 设置key和value 127.0.0.1:6379> set …...

SpringSecurity6 | 自定义登录页面

✅作者简介&#xff1a;大家好&#xff0c;我是Leo&#xff0c;热爱Java后端开发者&#xff0c;一个想要与大家共同进步的男人&#x1f609;&#x1f609; &#x1f34e;个人主页&#xff1a;Leo的博客 &#x1f49e;当前专栏&#xff1a; Java从入门到精通 ✨特色专栏&#xf…...

从单向链表中删除指定值的节点

输入一个单向链表和一个节点的值&#xff0c;从单向链表中删除等于该值的节点&#xff0c;删除后如果链表中无节点则返回空指针。 链表的值不能重复。构造过程&#xff0c;例如输入一行数据为:6 2 1 2 3 2 5 1 4 5 7 2 2则第一个参数6表示输入总共6个节点&#xff0c;第二个参数…...

Vue2与Vue3的语法对比

Vue2与Vue3的语法对比 Vue.js是一款流行的JavaScript框架&#xff0c;通过它可以更加轻松地构建Web用户界面。随着Vue.js的不断发展&#xff0c;Vue2的语法已经在很多应用中得到了广泛应用。而Vue3于2020年正式发布&#xff0c;带来了许多新的特性和改进&#xff0c;同时也带来…...

实时动作识别学习笔记

目录 yowo v2 yowof 判断是在干什么,不能获取细节信息 yowo v2 https://github.com/yjh0410/YOWOv2/blob/master/README_CN.md ModelClipmAPFPSweightYOWOv2-Nano1612.640ckptYOWOv2-Tiny...

5G常用简称

名称缩写全称非周期 信道状态信息参考信号aperidoc CSIAperidoc Channel State Information缓冲区状态报告BSRBuffer Status Report小区特定无线网络标识CS-RNTICell-Specific Radio Network Temporary Identifier主小区组MCGMaster Cell groupMCG的节点MNMasternode主小区PCel…...

自动化测试框架性能测试报告模板

一、项目概述 1.1 编写目的 本次测试报告&#xff0c;为自动化测试框架性能测试总结报告。目的在于总结我们课程所压测的目标系统的性能点、优化历史和可优化方向。 1.2 项目背景 我们公开课的性能测试目标系统。主要是用于我们课程自动化测试框架功能的实现&#xff0c;以及…...

【SpringBoot】解析Springboot事件机制,事件发布和监听

解析Springboot事件机制&#xff0c;事件发布和监听 一、Spring的事件是什么二、使用步骤2.1 依赖处理2.2 定义事件实体类2.3 定义事件监听类2.4 事件发布 三、异步调用3.1 启用异步调用3.2 监听器方法上添加 Async 注解 一、Spring的事件是什么 Spring的事件监听&#xff08;…...

华为ensp实验——基于全局地址池的DHCP组网实验

目录 前言实验目的实验内容实验结果 前言 该实验基于华为ensp&#xff0c;版本号是1.3.00.100 V100R003C00SPC100&#xff0c;只供学习和参考&#xff0c;不作任何商业用途。 具体的DHCP命令可以看系列文章链接&#xff0c;计算机网络实验&#xff08;华为eNSP模拟器&#xff…...

如何选择一款安全可靠的跨网安全数据交换系统?

随着网络和数据安全的重视程度增加&#xff0c;为了有效地保护内部的核心数据资产&#xff0c;普遍会采用内外网隔离的策略。像国内的政府机构、金融、能源电力、航空航天、医院等关乎国计民生的行业和领域均已进行了网络的隔离&#xff0c;将内部划分成不同的网段&#xff0c;…...

基于c++版本的数据结构改-python栈和队列思维总结

##栈部分-&#xff08;叠猫猫&#xff09; ##抽象数据类型栈的定义&#xff1a;是一种遵循先入后出的逻辑的线性数据结构。 换种方式去理解这种数据结构如果我们在一摞盘子中取到下面的盘子&#xff0c;我们首先要把最上面的盘子依次拿走&#xff0c;才可以继续拿下面的盘子&…...

算法通关村第七关—迭代实现二叉树的遍历(黄金)

迭代实现二叉树的遍历 迭代法实现前序遍历 前序遍历是中左右&#xff0c;如果还有左子树就一直向下找。完了之后再返回从最底层逐步向上向右找。不难写出如下代码&#xff1a;&#xff08;注意代码中&#xff0c;空节点不入栈&#xff09; public List<Integer>preorde…...

Java期末复习题之封装

点击返回标题->23年Java期末复习-CSDN博客 第1题. 定义一个类Person,定义name和age私有属性&#xff0c;定义有参的构造方法对name和age进行初始化。在测试类中创建该类的2个对象&#xff0c;姓名、年龄分别为lili、19和lucy、20&#xff0c;在屏幕打印出2个对象的姓名和年龄…...

湖科大计网:计算机网络概述

一、计算机网络的性能指标 一、速率 有时候数据量也认为是以10为底的&#xff0c;看怎么好算。&#xff08;具体吉大考试用什么待商榷&#xff09; 二、带宽 在模拟信号系统中带宽的含义&#xff0c;本课程中用到的地方是&#xff1a;香农定理和奈奎斯特定理公式的应用之中。 …...

后进先出(LIFO)详解

LIFO 是 Last In, First Out 的缩写&#xff0c;中文译为后进先出。这是一种数据结构的工作原则&#xff0c;类似于一摞盘子或一叠书本&#xff1a; 最后放进去的元素最先出来 -想象往筒状容器里放盘子&#xff1a; &#xff08;1&#xff09;你放进的最后一个盘子&#xff08…...

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…...

【力扣数据库知识手册笔记】索引

索引 索引的优缺点 优点1. 通过创建唯一性索引&#xff0c;可以保证数据库表中每一行数据的唯一性。2. 可以加快数据的检索速度&#xff08;创建索引的主要原因&#xff09;。3. 可以加速表和表之间的连接&#xff0c;实现数据的参考完整性。4. 可以在查询过程中&#xff0c;…...

Python:操作 Excel 折叠

💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 Python 操作 Excel 系列 读取单元格数据按行写入设置行高和列宽自动调整行高和列宽水平…...

Keil 中设置 STM32 Flash 和 RAM 地址详解

文章目录 Keil 中设置 STM32 Flash 和 RAM 地址详解一、Flash 和 RAM 配置界面(Target 选项卡)1. IROM1(用于配置 Flash)2. IRAM1(用于配置 RAM)二、链接器设置界面(Linker 选项卡)1. 勾选“Use Memory Layout from Target Dialog”2. 查看链接器参数(如果没有勾选上面…...

基于Docker Compose部署Java微服务项目

一. 创建根项目 根项目&#xff08;父项目&#xff09;主要用于依赖管理 一些需要注意的点&#xff1a; 打包方式需要为 pom<modules>里需要注册子模块不要引入maven的打包插件&#xff0c;否则打包时会出问题 <?xml version"1.0" encoding"UTF-8…...

《基于Apache Flink的流处理》笔记

思维导图 1-3 章 4-7章 8-11 章 参考资料 源码&#xff1a; https://github.com/streaming-with-flink 博客 https://flink.apache.org/bloghttps://www.ververica.com/blog 聚会及会议 https://flink-forward.orghttps://www.meetup.com/topics/apache-flink https://n…...

Python 包管理器 uv 介绍

Python 包管理器 uv 全面介绍 uv 是由 Astral&#xff08;热门工具 Ruff 的开发者&#xff09;推出的下一代高性能 Python 包管理器和构建工具&#xff0c;用 Rust 编写。它旨在解决传统工具&#xff08;如 pip、virtualenv、pip-tools&#xff09;的性能瓶颈&#xff0c;同时…...

【Redis】笔记|第8节|大厂高并发缓存架构实战与优化

缓存架构 代码结构 代码详情 功能点&#xff1a; 多级缓存&#xff0c;先查本地缓存&#xff0c;再查Redis&#xff0c;最后才查数据库热点数据重建逻辑使用分布式锁&#xff0c;二次查询更新缓存采用读写锁提升性能采用Redis的发布订阅机制通知所有实例更新本地缓存适用读多…...

DingDing机器人群消息推送

文章目录 1 新建机器人2 API文档说明3 代码编写 1 新建机器人 点击群设置 下滑到群管理的机器人&#xff0c;点击进入 添加机器人 选择自定义Webhook服务 点击添加 设置安全设置&#xff0c;详见说明文档 成功后&#xff0c;记录Webhook 2 API文档说明 点击设置说明 查看自…...