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

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() {}
}
  1. 泛型扩展属性

    // 只有实现了 Runnable 和 MyInterface 才能拥有此扩展属性
    val <T> T.id where T: Runnable, T: MyInterfaceget() = 123fun main() {MyClass().id
    }
    
  2. 泛型函数

    // 只有实现了 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>()
    }
    
  3. 泛型类或接口

    // 只有实现了 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 限制泛型输入输出

inout修饰泛型只能用在类或接口。
当一个泛型被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 限制泛型输入输出 定义 有时候我们会有这样的需求&#xff1a;一个类可以操作某一类型的对象&#xff0c;并且限定只有该类型的参数才能执行相关的操作。 如果我们直接指定该类型Int&#xff…...

Tomcat 面试题(一)

1. 简述什么是Tomcat &#xff1f; Tomcat是一个开源的Java Servlet容器&#xff0c;它实现了Java Servlet和JavaServer Pages (JSP)技术&#xff0c;提供了一个运行Java Web应用程序的平台。Tomcat由Apache软件基金会维护&#xff0c;并广泛用于开发和部署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.找出缺失和重复的数字&#xff1a;小数据&#xff1f;我选择暴力&#xff08;附优化方法清单&#xff1a;O(1)空间方法3&#xff09; 力扣题目链接&#xff1a;https://leetcode.cn/problems/find-missing-and-repeated-values/ 给你一个下标从 0 开始的二维…...

【运维】VMware Workstation 虚拟机内无网络的解决办法(或许可行)

【使用桥接模式】 【重置网络】 这个过程涉及管理Linux系统中的网络驱动程序和网络管理工具。以下是每个步骤的详细解释&#xff1a; 卸载网络驱动模块&#xff1a; sudo rmmod e1000 sudo rmmod e1000e sudo rmmod igb这些命令使用 rmmod&#xff08;remove module&#xff…...

如何使用Dora SDK完成Fragment流式切换和非流式切换

我想大家对Fragment都不陌生&#xff0c;它作为界面碎片被使用在Activity中&#xff0c;如果只是更换Activity中的一小部分界面&#xff0c;是没有必要再重新打开一个新的Activity的。有时&#xff0c;即使要更换完整的UI布局&#xff0c;也可以使用Fragment来切换界面。 何…...

低代码开发平台(Low-code Development Platform)的模块组成部分

低代码开发平台&#xff08;Low-code Development Platform&#xff09;的模块组成部分主要包括以下几个方面&#xff1a; 低代码开发平台的模块组成部分可以按照包含系统、模块、菜单组织操作行为等维度进行详细阐述。以下是从这些方面对平台模块组成部分的说明&#xff1a; …...

Java网络编程(上)

White graces&#xff1a;个人主页 &#x1f649;专栏推荐:Java入门知识&#x1f649; &#x1f649; 内容推荐:Java文件IO&#x1f649; &#x1f439;今日诗词:来如春梦几多时&#xff1f;去似朝云无觅处&#x1f439; ⛳️点赞 ☀️收藏⭐️关注&#x1f4ac;卑微小博主&a…...

Spring Kafka 之 @KafkaListener 注解详解

我们在开发的过程中当使用到kafka监听消费的时候会使用到KafkaListener注解&#xff0c;下面我们就介绍下它的常见属性和使用。 一、介绍 KafkaListener 是 Spring Kafka 提供的一个注解&#xff0c;用于声明一个方法作为 Kafka 消息的监听器 二、主要参数 1、topic 描述&…...

【量算分析工具-贴地距离】GeoServer改造Springboot番外系列九

【量算分析工具-概述】GeoServer改造Springboot番外系列三-CSDN博客 【量算分析工具-水平距离】GeoServer改造Springboot番外系列四-CSDN博客 【量算分析工具-水平面积】GeoServer改造Springboot番外系列五-CSDN博客 【量算分析工具-方位角】GeoServer改造Springboot番外系列…...

【linux】(1)文件操作及vi

文件和目录的创建 创建文件 touch 命令&#xff1a;创建一个新的空文件。 touch filename.txtecho 命令&#xff1a;创建一个文件并写入内容。 echo "Hello, World!" > filename.txtcat 命令&#xff1a;将内容写入文件。 cat > filename.txt然后输入内容&…...

【5】MySQL数据库备份-XtraBackup - 全量备份

MySQL数据库备份-XtraBackup-全量备份 前言环境版本 安装部署下载RPM 包二进制包 安装卸载 场景分析全量备份 | 恢复备份恢复综合 增量备份 | 恢复部分备份 | 恢复 前言 关于数据库备份的一些常见术语、工具等&#xff0c;可见《MySQL数据库-备份》章节&#xff0c;当前不再重…...

数据治理-数据标准演示

数据字典 数据标准-数据字典 词根 数据标准-词根 业务字典映射 数据标准-业务字典映射 标准文档 数据标准-标准文档...

基于Chisel的FPGA流水灯设计

Chisel流水灯 一、Chisel&#xff08;一&#xff09;什么是Chisel&#xff08;二&#xff09;Chisel能做什么&#xff08;三&#xff09;Chisel的使用&#xff08;四&#xff09;Chisel的优缺点1.优点2.缺点 二、流水灯设计 一、Chisel &#xff08;一&#xff09;什么是Chise…...

LabVIEW齿轮调制故障检测系统

LabVIEW齿轮调制故障检测系统 概述 开发了一种基于LabVIEW平台的齿轮调制故障检测系统&#xff0c;实现齿轮在恶劣工作条件下的故障振动信号的实时在线检测。系统利用LabVIEW的强大图形编程能力&#xff0c;结合Hilbert包络解调技术&#xff0c;对齿轮的振动信号进行精确分析…...

AI帮写:探索国内AI写作工具的创新与实用性

随着AI技术的快速发展&#xff0c;AI写作正成为创作的新风口。但是面对GPT-4这样的国际巨头&#xff0c;国内很多小伙伴往往望而却步&#xff0c;究其原因&#xff0c;就是它的使用门槛高&#xff0c;还有成本的考量。 不过&#xff0c;随着GPT技术的火热&#xff0c;国内也涌…...

n后问题 回溯笔记

问题描述 在nn格的棋盘上放置彼此不受攻击的n个皇后。 按照国际象棋的规则&#xff0c;皇后可以攻击与之处在同 一行或同一列或同一斜线上的棋子。n后问题等价于在nn格的棋盘上放置n个皇后&#xff0c;任何2个皇后不放在同一行或同一列或同一斜线上。 代码 import java.uti…...

简述Java中实现Socket通信的步骤

在Java中&#xff0c;实现Socket通信通常涉及客户端和服务器端两个角色。以下是它们各自的基本步骤&#xff1a; 服务器端&#xff08;Server&#xff09; 创建ServerSocket对象&#xff1a; 在服务器端&#xff0c;首先需要创建一个ServerSocket对象。这个对象会监听来自客户…...

Asp.Net Core 实现分片下载的最简单方式

技术群里的朋友遇到了这个问题&#xff0c;起初的原因是他对文件增加了一个属性配置 fileResult.EnableRangeProcessing true;这个属性我从未遇到过&#xff0c;然后&#xff0c;去F1查看这个属性的描述信息也依然少的可怜&#xff0c;只有简单的描述为(获取或设置为 启用范围…...

[Mac软件]Leech for Mac v3.2 - 轻量级mac下载工具

黑果魏叔推荐Leech是由Many Tricks开发的适用于Mac OS X的轻量级且功能强大的下载管理器。 Leech让您完全控制下载&#xff0c;并与浏览器完全集成。您可以将下载排队&#xff0c;暂停和恢复&#xff0c;从受密码保护的服务器下载&#xff0c;并将密码存储在系统范围的安全钥匙…...

脑机新手指南(八):OpenBCI_GUI:从环境搭建到数据可视化(下)

一、数据处理与分析实战 &#xff08;一&#xff09;实时滤波与参数调整 基础滤波操作 60Hz 工频滤波&#xff1a;勾选界面右侧 “60Hz” 复选框&#xff0c;可有效抑制电网干扰&#xff08;适用于北美地区&#xff0c;欧洲用户可调整为 50Hz&#xff09;。 平滑处理&…...

黑马Mybatis

Mybatis 表现层&#xff1a;页面展示 业务层&#xff1a;逻辑处理 持久层&#xff1a;持久数据化保存 在这里插入图片描述 Mybatis快速入门 ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/6501c2109c4442118ceb6014725e48e4.png //logback.xml <?xml ver…...

《Qt C++ 与 OpenCV:解锁视频播放程序设计的奥秘》

引言:探索视频播放程序设计之旅 在当今数字化时代,多媒体应用已渗透到我们生活的方方面面,从日常的视频娱乐到专业的视频监控、视频会议系统,视频播放程序作为多媒体应用的核心组成部分,扮演着至关重要的角色。无论是在个人电脑、移动设备还是智能电视等平台上,用户都期望…...

【入坑系列】TiDB 强制索引在不同库下不生效问题

文章目录 背景SQL 优化情况线上SQL运行情况分析怀疑1:执行计划绑定问题?尝试:SHOW WARNINGS 查看警告探索 TiDB 的 USE_INDEX 写法Hint 不生效问题排查解决参考背景 项目中使用 TiDB 数据库,并对 SQL 进行优化了,添加了强制索引。 UAT 环境已经生效,但 PROD 环境强制索…...

多种风格导航菜单 HTML 实现(附源码)

下面我将为您展示 6 种不同风格的导航菜单实现&#xff0c;每种都包含完整 HTML、CSS 和 JavaScript 代码。 1. 简约水平导航栏 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport&qu…...

Spring AI与Spring Modulith核心技术解析

Spring AI核心架构解析 Spring AI&#xff08;https://spring.io/projects/spring-ai&#xff09;作为Spring生态中的AI集成框架&#xff0c;其核心设计理念是通过模块化架构降低AI应用的开发复杂度。与Python生态中的LangChain/LlamaIndex等工具类似&#xff0c;但特别为多语…...

Kafka主题运维全指南:从基础配置到故障处理

#作者&#xff1a;张桐瑞 文章目录 主题日常管理1. 修改主题分区。2. 修改主题级别参数。3. 变更副本数。4. 修改主题限速。5.主题分区迁移。6. 常见主题错误处理常见错误1&#xff1a;主题删除失败。常见错误2&#xff1a;__consumer_offsets占用太多的磁盘。 主题日常管理 …...

Python 训练营打卡 Day 47

注意力热力图可视化 在day 46代码的基础上&#xff0c;对比不同卷积层热力图可视化的结果 import torch import torch.nn as nn import torch.optim as optim from torchvision import datasets, transforms from torch.utils.data import DataLoader import matplotlib.pypl…...

【Linux】Linux安装并配置RabbitMQ

目录 1. 安装 Erlang 2. 安装 RabbitMQ 2.1.添加 RabbitMQ 仓库 2.2.安装 RabbitMQ 3.配置 3.1.启动和管理服务 4. 访问管理界面 5.安装问题 6.修改密码 7.修改端口 7.1.找到文件 7.2.修改文件 1. 安装 Erlang 由于 RabbitMQ 是用 Erlang 编写的&#xff0c;需要先安…...

boost::filesystem::path文件路径使用详解和示例

boost::filesystem::path 是 Boost 库中用于跨平台操作文件路径的类&#xff0c;封装了路径的拼接、分割、提取、判断等常用功能。下面是对它的使用详解&#xff0c;包括常用接口与完整示例。 1. 引入头文件与命名空间 #include <boost/filesystem.hpp> namespace fs b…...