Kotlin 泛型
文章目录
- 定义
- 泛型属性
- 泛型函数
- 泛型类或接口
- where 声明多个约束
- 泛型具体化
- in、out 限制泛型输入输出
定义
有时候我们会有这样的需求:一个类可以操作某一类型的对象,并且限定只有该类型的参数才能执行相关的操作。
如果我们直接指定该类型Int
,确实可以实现该操作,但是换一个类型就得再重新定义一个类:
class MyArray {fun add(int: Int) {}fun delete(int: Int) {}
}fun main() {val myArray = MyArray()myArray.add(1)myArray.delete(1)
}
如果我们将类型改为Any
,又会导致可操作参数太过笼统,还容易出现各种问题:
class MyArray {fun add(any: Any) {}fun delete(any: Any) {}
}fun main() {val myArray = MyArray()myArray.add(1)myArray.delete("abc")
}
这时候我们需要引入泛型。泛型的定义方式如下:
<泛型名[: 泛型所属类型], ...>
一般情况下,泛型名会是单个的大写字母,很多时候是T
,而泛型所属类型可以指定泛型是某个类/接口或者其子类/实现(当然,单独这样写做不了什么,请继续往下看):
<T>
<T: Runnable>
<K, V>
泛型属性
在 Kotlin 中,可以为一个类定义扩展属性:
class MyClassval MyClass.name get() = "MyClass"
如果我们要指定是任何类都有的属性,则可以泛型。
泛型属性只能用在扩展,声明时需要将泛型定义放置在val|var
后,变量名前。
val <T> T.name get() = "Any"fun main() {val int = 1print(int.name)
}
Any
也可以限制该泛型是Runnable
接口或其实现。
val <T: Runnable> T.name get() = "Runnable"fun main() {val int = 1// print(int.name) 不可访问print(Runnable { }.name)
}
Runnable
泛型函数
我们也可以为函数泛型,与泛型扩展属性类似,泛型定义需要写在fun
和函数名之间(意外发现此时as
如果无法转换不会抛出异常,而是返回any
):
fun <T> cast(any: Any) = any as Tfun main() {cast<List<Double>>(listOf(1, 2)).forEach { print(it); print(", ") }
}
1.0, 2.0,
Note:泛型可以作为函数接收者、参数、返回值类型的一部分。如果一个泛型函数在调用时可以通过传入的参数类型或返回值声明确定泛型的类型,则调用时泛型的类型可以缺省,否则需要指明其类型
函数名<泛型类型, ...>()
(泛型类与此类似)。
声明两个泛型(涉及到的 Kotlin 一些便捷的习语我们将在后续文章学习):
fun <K, V> hashMap(vararg pairs: Pair<K, V>) =HashMap<K, V>().apply { putAll(pairs) }fun main() {print(hashMap("key" to 1))
}
{key=1}
泛型扩展函数:
// List 类是一个泛型类
// 可以将它的泛型指定为函数的泛型
fun <T> List<T>.print() = print(this)fun main() {listOf(1, 2, 3).print()
}
[1, 2, 3]
fun <T: Comparable<Int>> T.isGreaterZero() = this > 0fun <T: Comparable<Int>> T.isGreaterZero() = this > 0fun main() {val int = 1// Int 实现了 Comparable<Int>// 因此可以调用print(int.isGreaterZero())
}
true
作为返回值:
fun <T> Any.toType() = this as Tfun main() {// 没有指定变量类型,无法推断泛型的类型// 因此调用时需要声明泛型类型val doubleList = listOf<String>().toType<List<Double>>()val intList: List<Double> = listOf<String>().toType()
}
泛型类或接口
在定义泛型类或接口时,需要将泛型定义放置在类名之后,括号之前。
泛型函数与接口是类似的,这里以类为例。
class Group<T>(vararg elements: T) {private val value = mutableListOf(*elements)fun add(element: T) = value.add(element)fun remove(element: T) = value.remove(element)// 重写 toString,调用 print 时即可打印出列表override fun toString(): String = value.toString()
}fun main() {val group = Group(1, 2, 3) // Group<Int>(1, 2, 3)println(group)group.remove(2)println(group)group.remove(4)print(group)
}
[1, 2, 3]
[1, 3]
[1, 3]
Note:
vararg
用于定义数量不定的参数,vararg element: Int
表示你可以传入任意数量(0…无限大)的Int
类型,在内部访问element
会获取到一个数组Array<Int>
,而使用*
可以将其展开为vararg
(也就是原始传入的任意数量的Int
),可以用于传入另一个需要vararg
的函数。
使用*
可以指代所有泛型(下方例子中相当于Any?
,*
无法用在函数调用上):
fun List<*>.print() = print(this)fun main() {listOf(1, 2, 3).print()println()listOf("a", "b", "c").print()
}
[1, 2, 3]
[a, b, c]
where 声明多个约束
我们可以使用<T: 类型>
来约束泛型为某一类型或其子类。但有时候我们想要将其约束于多个类型,可以使用where
来实现(此时<T: 类型>
中的类型也应该一并移到where
处):
我们先定义一个接口和一个类,后面的例子会反复用到:
interface MyInterfaceclass MyClass : Runnable, MyInterface {override fun run() {}
}
-
泛型扩展属性
// 只有实现了 Runnable 和 MyInterface 才能拥有此扩展属性 val <T> T.id where T: Runnable, T: MyInterfaceget() = 123fun main() {MyClass().id }
-
泛型函数
// 只有实现了 Runnable 和 MyInterface 才能拥有此扩展函数 fun <T> T.getId() where T : Runnable, T : MyInterface = 123// 只有泛型 T 实现了 Runnable 和 MyInterface 才能使用此函数 fun <T> getName() where T : Runnable, T : MyInterface ="Runnable&MyInterface"fun main() {MyClass().getId()getName<MyClass>() }
-
泛型类或接口
// 只有实现了 Runnable 和 MyInterface 才能作为构造参数 value 传入 class MyContainer<T>(val value: T) where T : Runnable, T : MyInterfacefun main() {MyContainer(MyClass()) }
泛型具体化
有时候我们需要判断泛型的类型,会想到用类型::class
获取对应的KClass
进行对比。而当该类型为泛型时,我们需要将其具象化reified
,通常该声明只能用于函数,并且需要与inline
搭配使用:
inline fun <reified T> T.isInt() = T::class == Int::classfun main() {println(1.isInt())print(false.isInt())
}
true
false
in、out 限制泛型输入输出
in
和out
修饰泛型只能用在类或接口。
当一个泛型被in
修饰时,表明该类型只能作为对象属性、方法的输入(赋值、传参):
class Group<in T>(vararg elements: T) {// 因为泛型声明为 in,属性类型中包含 T 可见性只能为 privateprivate val value = mutableListOf(*elements)fun add(element: T) {}// 因为泛型声明为 in,不能定义返回 T 类型的方法// fun get(index: Int) = value[index]
}
当一个泛型被in
修饰时,表明该类型只能作为对象属性、方法的输出(获取值、函数返回):
class Group<out T>(vararg elements: T) {// 因为泛型声明为 out,而 MutableList<T> 具有 in 和 out// 若将其公开,将会允许 in T,例如 value.add(element: T)// 属性类型中包含 T 可见性只能为 privateprivate val value = mutableListOf(*elements)// 因为泛型声明为 out,不能作为参数类型// fun add(element: T) {}fun get(index: Int) = value.get(index)
}
Note:注意是针对对象而言,如果是一个类声明了
out
泛型,仍然可以将该泛型作为构造函数中的参数类型。class Group<out T>(vararg elements: T)
相关文章:
Kotlin 泛型
文章目录 定义泛型属性泛型函数泛型类或接口 where 声明多个约束泛型具体化in、out 限制泛型输入输出 定义 有时候我们会有这样的需求:一个类可以操作某一类型的对象,并且限定只有该类型的参数才能执行相关的操作。 如果我们直接指定该类型Intÿ…...
Tomcat 面试题(一)
1. 简述什么是Tomcat ? Tomcat是一个开源的Java Servlet容器,它实现了Java Servlet和JavaServer Pages (JSP)技术,提供了一个运行Java Web应用程序的平台。Tomcat由Apache软件基金会维护,并广泛用于开发和部署Web应用程序。 Tom…...
跟踪一个Pytorch Module在训练过程中的内存分配情况
跟踪一个Pytorch Module在训练过程中的内存分配情况 代码输出 目的:跟踪一个Pytorch Module在训练过程中的内存分配情况 方法: 1.通过pre_hook module的来区分module的边界 2.通过__torch_dispatch__拦截所有的aten算子,计算在该算子中新创建tensor的总内存占用量 3.通过tensor…...
LeetCode 2965.找出缺失和重复的数字:小数据?我选择暴力(附优化方法清单:O(1)空间方法×3)
【LetMeFly】2965.找出缺失和重复的数字:小数据?我选择暴力(附优化方法清单:O(1)空间方法3) 力扣题目链接:https://leetcode.cn/problems/find-missing-and-repeated-values/ 给你一个下标从 0 开始的二维…...
【运维】VMware Workstation 虚拟机内无网络的解决办法(或许可行)
【使用桥接模式】 【重置网络】 这个过程涉及管理Linux系统中的网络驱动程序和网络管理工具。以下是每个步骤的详细解释: 卸载网络驱动模块: sudo rmmod e1000 sudo rmmod e1000e sudo rmmod igb这些命令使用 rmmod(remove moduleÿ…...
如何使用Dora SDK完成Fragment流式切换和非流式切换
我想大家对Fragment都不陌生,它作为界面碎片被使用在Activity中,如果只是更换Activity中的一小部分界面,是没有必要再重新打开一个新的Activity的。有时,即使要更换完整的UI布局,也可以使用Fragment来切换界面。 何…...
低代码开发平台(Low-code Development Platform)的模块组成部分
低代码开发平台(Low-code Development Platform)的模块组成部分主要包括以下几个方面: 低代码开发平台的模块组成部分可以按照包含系统、模块、菜单组织操作行为等维度进行详细阐述。以下是从这些方面对平台模块组成部分的说明: …...
Java网络编程(上)
White graces:个人主页 🙉专栏推荐:Java入门知识🙉 🙉 内容推荐:Java文件IO🙉 🐹今日诗词:来如春梦几多时?去似朝云无觅处🐹 ⛳️点赞 ☀️收藏⭐️关注💬卑微小博主&a…...
Spring Kafka 之 @KafkaListener 注解详解
我们在开发的过程中当使用到kafka监听消费的时候会使用到KafkaListener注解,下面我们就介绍下它的常见属性和使用。 一、介绍 KafkaListener 是 Spring Kafka 提供的一个注解,用于声明一个方法作为 Kafka 消息的监听器 二、主要参数 1、topic 描述&…...
【量算分析工具-贴地距离】GeoServer改造Springboot番外系列九
【量算分析工具-概述】GeoServer改造Springboot番外系列三-CSDN博客 【量算分析工具-水平距离】GeoServer改造Springboot番外系列四-CSDN博客 【量算分析工具-水平面积】GeoServer改造Springboot番外系列五-CSDN博客 【量算分析工具-方位角】GeoServer改造Springboot番外系列…...
【linux】(1)文件操作及vi
文件和目录的创建 创建文件 touch 命令:创建一个新的空文件。 touch filename.txtecho 命令:创建一个文件并写入内容。 echo "Hello, World!" > filename.txtcat 命令:将内容写入文件。 cat > filename.txt然后输入内容&…...
【5】MySQL数据库备份-XtraBackup - 全量备份
MySQL数据库备份-XtraBackup-全量备份 前言环境版本 安装部署下载RPM 包二进制包 安装卸载 场景分析全量备份 | 恢复备份恢复综合 增量备份 | 恢复部分备份 | 恢复 前言 关于数据库备份的一些常见术语、工具等,可见《MySQL数据库-备份》章节,当前不再重…...
数据治理-数据标准演示
数据字典 数据标准-数据字典 词根 数据标准-词根 业务字典映射 数据标准-业务字典映射 标准文档 数据标准-标准文档...
基于Chisel的FPGA流水灯设计
Chisel流水灯 一、Chisel(一)什么是Chisel(二)Chisel能做什么(三)Chisel的使用(四)Chisel的优缺点1.优点2.缺点 二、流水灯设计 一、Chisel (一)什么是Chise…...
LabVIEW齿轮调制故障检测系统
LabVIEW齿轮调制故障检测系统 概述 开发了一种基于LabVIEW平台的齿轮调制故障检测系统,实现齿轮在恶劣工作条件下的故障振动信号的实时在线检测。系统利用LabVIEW的强大图形编程能力,结合Hilbert包络解调技术,对齿轮的振动信号进行精确分析…...
AI帮写:探索国内AI写作工具的创新与实用性
随着AI技术的快速发展,AI写作正成为创作的新风口。但是面对GPT-4这样的国际巨头,国内很多小伙伴往往望而却步,究其原因,就是它的使用门槛高,还有成本的考量。 不过,随着GPT技术的火热,国内也涌…...
n后问题 回溯笔记
问题描述 在nn格的棋盘上放置彼此不受攻击的n个皇后。 按照国际象棋的规则,皇后可以攻击与之处在同 一行或同一列或同一斜线上的棋子。n后问题等价于在nn格的棋盘上放置n个皇后,任何2个皇后不放在同一行或同一列或同一斜线上。 代码 import java.uti…...
简述Java中实现Socket通信的步骤
在Java中,实现Socket通信通常涉及客户端和服务器端两个角色。以下是它们各自的基本步骤: 服务器端(Server) 创建ServerSocket对象: 在服务器端,首先需要创建一个ServerSocket对象。这个对象会监听来自客户…...
Asp.Net Core 实现分片下载的最简单方式
技术群里的朋友遇到了这个问题,起初的原因是他对文件增加了一个属性配置 fileResult.EnableRangeProcessing true;这个属性我从未遇到过,然后,去F1查看这个属性的描述信息也依然少的可怜,只有简单的描述为(获取或设置为 启用范围…...
[Mac软件]Leech for Mac v3.2 - 轻量级mac下载工具
黑果魏叔推荐Leech是由Many Tricks开发的适用于Mac OS X的轻量级且功能强大的下载管理器。 Leech让您完全控制下载,并与浏览器完全集成。您可以将下载排队,暂停和恢复,从受密码保护的服务器下载,并将密码存储在系统范围的安全钥匙…...
留给“端侧大模型”的时间不多了
端侧大模型(Edge AI models),也就是只在设备本地(如智能手机、IoT设备、嵌入式系统等)运行的大模型,过去一两年来非常流行。 具体表现在,终端设备厂商,如苹果、荣耀、小米、OV等&…...
Pytest框架中的Setup和Teardown功能
在 pytest 测试框架中,setup 和 teardown是用于在每个测试函数之前和之后执行设置和清理的动作,而pytest 实际上并没有内置的 setup 和 teardown 函数,而是使用了一些装饰器或钩子函数来实现类似的功能。 学习目录 钩子函数(Hook…...
yolov10/v8 loss详解
v10出了就想看看它的loss设计有什么不同,看下来由于v8和v10的loss部分基本一致就放一起了。 v10的论文笔记,还没看的可以看看,初步尝试耗时确实有提升 好记性不如烂笔头,还是得记录一下,以免忘了,废话结束…...
Typescript高级: 深入理解infer关键字
概述 在 TS 中,infer 是一个高级类型操作,特别是条件类型和映射类型中非常有用的关键字它在泛型中使用也会是一个强大工具,增强了类型推断的能力,让开发者更灵活地处理和操作类型它允许在泛型类型推导过程中捕获一个具体的类型&a…...
JQC-3FF-S-Z 继电器模块使用(arduino)
前言 继电器模块可以控制电流的接通和非接通状态,和开关一样。实际上是用小电流去控制大电流运作的一种“自动开关” 本文只是简单使用继电器模块做一个 led 点亮和熄灭的案例,结合案例可以和 nodemcu 等板子结合做出远程控制开关。 材料准备 杜邦线…...
黑马一站制造数仓实战2
问题 DG连接问题 原理:JDBC:用Java代码连接数据库 Hive/SparkSQL:端口有区别 可以为同一个端口,只要不在同一台机器 项目:一台机器 HiveServer:10000 hiveserver.port 10000 SparkSQL:10001…...
网络I/O模型
网络I/O模型 同步I/O阻塞I/O非阻塞I/OI/O多路复用select函数接口示例 poll函数接口示例 poll 和 select 的区别epoll原理:示例 异步I/O 同步I/O 阻塞I/O 一个基本的C/S模型如下图所图:其中 listen()、connect()、write()、read() 都是阻塞I/O࿰…...
Docker 简介和安装
目录 Docker 是什么 跟普通虚拟机的对比 打包、分发、部署 Docker 部署的优势 Docker 通常用来做什么 重要概念:镜像、容器 安装 镜像加速源 Docker 是什么 Docker 是一个应用打包、分发、部署的工具 你也可以把它理解为一个轻量的虚拟机,它只虚…...
【源码】Spring Data JPA原理解析之Repository自定义方法命名规则执行原理(二)
Spring Data JPA系列 1、SpringBoot集成JPA及基本使用 2、Spring Data JPA Criteria查询、部分字段查询 3、Spring Data JPA数据批量插入、批量更新真的用对了吗 4、Spring Data JPA的一对一、LazyInitializationException异常、一对多、多对多操作 5、Spring Data JPA自定…...
Vue前端中从后端获取图片验证码
前端发送请求 <template><el-form :model"user" :rules"rules" ref"userForm" class"login" label-width"auto" style"max-width: 600px"><el-form-item label"用户名" prop"name…...
【源码】多语言H5聊天室/thinkphp多国语言即时通讯/H5聊天室源码/在线聊天/全开源
多语言聊天室系统,可当即时通讯用,系统默认无需注册即可进入群聊天,全开源 【海外聊天室】多语言H5聊天室/thinkphp多国语言即时通讯/H5聊天室源码/在线聊天/全开源 - 吾爱资源网...
gitlab 创建 ssh 和 token
文章目录 一、创建ssh key二、将密钥内容复制到gitlab三、创建token 一、创建ssh key 打开控制台cmd,执行命令 ssh-keygen -t rsa -C xxxxx xxxxx是你自己的邮箱 C:\Users\xx\.ssh 目录下会创建一个名为id_rsa.pub的文件,用记事本打开,并…...
Docker - Kafka
博文目录 文章目录 说明命令 说明 Docker Hub - bitnami/kafka Docker Hub - apache/kafka Kafka QuickStart Kafka 目前没有 Docker 官方镜像, 目前拉取次数最多的是 bitnami/kafka, Apache 提供的是 apache/kafka (更新最及时), 本文使用 bitnami/kafka bitnami/kafka 镜像…...
一键实现文件夹批量高效重命名:轻松运用随机一个字母命名,让文件管理焕然一新!
在数字化时代,文件夹管理是我们日常生活和工作中不可或缺的一部分。然而,随着文件数量的不断增加,文件夹命名的繁琐和重复成为了一个让人头疼的问题。你是否曾因为手动一个个重命名文件夹而感到枯燥乏味?你是否曾渴望有一种方法能…...
Vue3项目练习详细步骤(第二部分:主页面搭建)
主页面搭建 页面主体结构 路由 子路由 主页面搭建 页面主体结构 在vuews目录下新建Layout.vue文件 主页面内容主体代码 <script setup> import {Management,Promotion,UserFilled,User,Crop,EditPen,SwitchButton,CaretBottom } from element-plus/icons-vue imp…...
[个人总结]-java常用方法
1.获取项目根路径 user.dir是一个系统属性,表示用户当前的工作目录,大多数情况下,用户的当前工作目录就是java项目的根目录(src文件的同级路径) System.getProperty("user.dir") 结果:D:\code…...
什么是Java泛型?它有什么作用
Java泛型(Generics)是一种允许在定义类、接口和方法时使用类型参数的机制。泛型提供了一种机制,使得代码可以对多种类型的对象进行操作,而无需进行类型转换。 Java泛型的作用 类型安全:通过在编译时进行类型检查&…...
[机缘参悟-197] - 《道家-水木然人间清醒1》读书笔记 -21-看问题从现象到本质的层次
目录 1. 现象层: 2. 关联层: 3. 原因层: 4. 本质层: 5. 解决方案层: 6. 设计实现层: 7. 泛化: 8. 创新与发现: 看问题从现象到本质的层次是一个逐步深入、由表及里的过程。这…...
AIGC商业案例实操课,发觉其创造和商业的无限可能,Ai技术在行业应用新的商机
课程下载:https://download.csdn.net/download/m0_66047725/89307523 更多资源下载:关注我。 课程内容 1 AI为什么火 。写在课程前面的寄语 。AIGC标志性事件:太空歌剧院 。AI人工智能为什么这么火 ,AI人工智能发展历程 。聊天AI会取…...
Java学习路径图
1.学习路径 JAVA架构师学习路径 2.路径拆解 2.1 Spring 2.1.1 SpringBoot原理 SpringBoot2学习视频 SpringBoot2笔记 SpringBoo2代码 2.2.2 SpringBoot项目 《谷粒商城》学习视频...
文章解读与仿真程序复现思路——电力系统自动化EI\CSCD\北大核心《考虑动态定价的新能源汽车能源站优化运行》
本专栏栏目提供文章与程序复现思路,具体已有的论文与论文源程序可翻阅本博主免费的专栏栏目《论文与完整程序》 论文与完整源程序_电网论文源程序的博客-CSDN博客https://blog.csdn.net/liang674027206/category_12531414.html 电网论文源程序-CSDN博客电网论文源…...
【简单讲解下Fine-tuning BERT,什么是Fine-tuning BERT?】
🎥博主:程序员不想YY啊 💫CSDN优质创作者,CSDN实力新星,CSDN博客专家 🤗点赞🎈收藏⭐再看💫养成习惯 ✨希望本文对您有所裨益,如有不足之处,欢迎在评论区提出…...
Docker搭建Redis主从 + Redis哨兵模式(一主一从俩哨兵)
我这里是搭建一主一从,俩哨兵,准备两台服务器,分别安装docker 我这里有两台centos服务器 主服务器IP:192.168.252.134 从服务器IP:192.168.252.135 1.两台服务器分别拉取redis镜像 docker pull redis 2.查看镜像 d…...
Three.js——tween动画、光线投射拾取、加载.obj/.mtl外部文件、使用相机控制器
个人简介 👀个人主页: 前端杂货铺 ⚡开源项目: rich-vue3 (基于 Vue3 TS Pinia Element Plus Spring全家桶 MySQL) 🙋♂️学习方向: 主攻前端方向,正逐渐往全干发展 …...
内网渗透-在HTTP协议层面绕过WAF
进入正题,随着安全意思增强,各企业对自己的网站也更加注重安全性。但很多web应用因为老旧,或贪图方便想以最小代价保证应用安全,就只仅仅给服务器安装waf。 本次从协议层面绕过waf实验用sql注入演示,但不限于实际应用…...
qt QGroupBox radiobutton
QGroupBox 显示文本:属性 title 加载radiobutton if (jsonObject.contains("startEndTogether") && jsonObject["startEndTogether"].isString()) {QString selectedButton jsonObject["startEndTogether"].toString();//…...
jetson nano onnxruntime 安装
安装说明: onnxruntime 依赖cuda、cudnn版本,可onnxruntime查找对应关系。但可能会出现jetpack中的cuda和cudnn的版本无法查找到对应版本的onnxruntime的问题。 解决方法: 通过Jetson Zoo下载相应的whl包直接安装。...
图形学初识--屏幕空间变换
文章目录 前言正文为什么需要屏幕空间变换?什么是屏幕空间变换?屏幕空间变换矩阵如何推导?问题描述步骤描述 结尾:喜欢的小伙伴点点关注赞哦! 前言 前面章节主要讲解了视图变换和投影变换,此时距离在屏幕空间显示也就…...
爬楼梯 - LeetCode 热题 81
大家好!我是曾续缘😇 今天是《LeetCode 热题 100》系列 发车第 81 天 动态规划第 1 题 ❤️点赞 👍 收藏 ⭐再看,养成习惯 爬楼梯 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法…...
详解 Spark 核心编程之 RDD 分区器
一、RDD 分区器简介 Spark 分区器的父类是 Partitioner 抽象类分区器直接决定了 RDD 中分区的个数、RDD 中每条数据经过 Shuffle 后进入哪个分区,进而决定了 Reduce 的个数只有 Key-Value 类型的 RDD 才有分区器,非 Key-Value 类型的 RDD 分区的值是 No…...