Kotlin快速入门系列8

Kotlin的泛型

与Java一样,Kotlin也提供泛型。泛型,即 "参数化类型",将类型参数化,可以用在类,接口,方法上。可以为类型安全提供保证,消除类型强转的烦恼。声明泛型类的格式如下:

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

用泛型创建类的实例的时候需要指定类型参数:

val box: Box<Int> = Box<Int>(100)
// 或者
val box = Box(100) // 声明泛型类时候编译器会进行类型推断,100的类型是Int,所以编译器知道我们说的是 Box<Int>。

定义泛型类型变量,可以完整地写明类型参数。如果定义泛型类的时候指定了泛型类型,则编译器可以自动推断类型参数,定义时就可以省略类型参数。

fun <T> boxIn(value: T) = Box(value)val box4 = boxIn<Int>(1)
val box5 = boxIn(1)     // 编译器会自动进行类型推断

在调用泛型函数时,如果可以推断参数类型,就可以省略泛型参数。

例如如下示例,泛型函数根据传入的不同类型做相应处理:

fun <T> printType(content: T) {when (content) {is Int -> println("整型参数为 $content")is String -> println("字符串参数转换为大写:${content.toUpperCase()}")else -> println("传入参数 T 既不是整型,也不是字符串")}
}fun main(args: Array<String>) {val num = 111val name = "Weyen"val bool = trueprintType(num)    // 整型printType(name)   // 字符串类型printType(bool)   // 布尔型
}

对应的输出结果为:

泛型约束

跟Java一样,Kotlin也拥有泛型约束。在Java中,使用extends关键字指明上界。在kotlin中使用:对泛型的类型上限进行约束。最常见的约束是上界(upper bound)。

例如,下面的代码中,调用num()函数时,传入的参数只能是Number及其子类,如果是其他类型,则会报错:

fun <T : Number> sum(vararg param: T) = param.sumByDouble { it.toDouble() }
fun main() {val va1 = sum(1,10,0.6)val va2 = sum(1,10,"kotlin") // 这里会提示编译错误
}

默认的上界是Any?。(注意,这里是Any?不是Any。Any 类似于 Java 中的 Object,它是所有非空类型的超类型。但是 Any 不能保存 null 值,如果需要 null 作为变量的一部分,则需要使用 Any?。Any?是 Any 的超类型,所以 Kotlin 默认的上界是 Any?)

如果有多个上界约束条件,可以用 where 子句:

open class ClassA
interface InterfaceB
class TypeClass<T>(var variable: Class<T>) where T : ClassA, T : InterfaceB

型变

Kotlin 中没有像java一样的<? extends T>这样的通配符,也没有父类向子类转换,取而代之的是两个其他的东西:声明处型变(declaration-site variance)与类型投影(type projections)。

声明处型变声明处的类型变异使用协变注解修饰符:in、out (消费者 in, 生产者 out。也可以这样理解:in,就是只能作为传入参数的参数类型;out, 就是只能作为返回类型参数的参数类型。)

相对于Java的概念:

out 协变:类型向上转换,像java中的子类向父类转换。

in 逆变:类型向下转换,父类向子类转换。

协变类型参数只能用作输出,可以作为返回值类型但是无法作为入参的类型:

// 支持协变的类
class KotlinChange<out A>(val demo: A) {fun foo(): A {return demo}
}fun main(args: Array<String>) {var strCo: KotlinChange<String> = KotlinChange("a")var anyCo: KotlinChange<Any> = KotlinChange<Any>("b")anyCo = strCoprintln(anyCo.foo())   // 对应的控制台输出 a
}

逆变类型参数只能用作输入,可以作为入参的类型但是无法作为返回值的类型:

// 这个类支持逆变,注意这里的in
class KotlinChange<in A>(num: A) {fun foo(num: A) {}
}fun main(args: Array<String>) {var strDCo = KotlinChange("a")var anyDCo = KotlinChange<Any>("b")strDCo = anyDCoprintln(strDCo.foo())  //这里就会报错了
}

星号投影(star-projection)

(星号(型)投影(射),单词翻译过来的叫啥都有,能明白意思就行)

在Kotlin 的泛型封装里,会出现 <*> ,这称为星号投影语法,用来表明"不知道关于泛型实参的任何信息"。

<*>星号投影,表示“不知道关于泛型实参的任何信息”,在修饰容器时,因为不知道是哪个类型,所以并不能向容器中写入任何东西(写入的任何值都可能会违反调用代码的期望)。读取值是可以的,因为所有存储在列表中的值都是Any?的子类。<*>星型投影,修饰的容器(比如:MutableList,MutableMap ),只能读不能写。 相当于<out Any?>。

比如:MutableList<*> 表示的是 MutableList<out Any?>

如果一个泛型类型中存在多个类型参数, 那么每个类型参数都可以单独的投影. 比如, 如果类型定义为interface Function<in T, out U> , 那么可以出现以下几种星号投影:

1、Function<*, String> , 代表 Function<in Nothing, String> ;

2、Function<Int, *> , 代表 Function<Int, out Any?> ;

3、Function<, > , 代表 Function<in Nothing, out Any?> 。

如下示例:

class KotlinBean<T>(val t: T, val t2 : T, val t3 : T)
class FruitBean(var name : String)
fun main(args: Array<String>) {val a1: KotlinBean<*> = KotlinBean(12, "String", FruitBean("苹果"))   //星号投影val a2: KotlinBean<Any?> = KotlinBean(12, "String", FruitBean("苹果"))   //和a1是一样的val apple = a1.t3    //参数类型为Anyprintln(apple)val apple2 = apple as FruitBean   //强转成FruitBean类println(apple2.name)//使用数组val list:ArrayList<*> = arrayListOf("String",1,3.14f,FruitBean("苹果"))for (item in list){println(item)}
}

对应的输出为:

这时我们可以换个角度理解,关于星号投影,其实就是*代指了所有类型,相当于Any?。

End,如有问题请留言讨论。

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

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

相关文章

UDP/TCP协议特点

1.前置知识 定义应用层协议 1.确定客户端和服务端要传递哪些信息 2.约定传输格式 网络上传输的一般是二进制数据/字符串 结构化数据转二进制/字符串 称为序列化 反之称之为反序列化 下面就是传输层了 在TCP/IP协议中,我们以 目的端口,目的IP 源端口 源IP 协议号这样一个五…

202413读书笔记|《好好恋爱是件正经事》——希望我们的故事永远崭新得像刚刚开始,永远未完待续

202413读书笔记|《好好恋爱是件正经事》——希望我们的故事永远崭新得像刚刚开始&#xff0c;永远未完待续 明亮的色彩&#xff0c;小红和小绿&#xff0c;哲理又日常治愈的文字&#xff0c;明快的线条&#xff0c;丰富的背景色&#xff0c;星星点点的⭐️斑斓点缀。 是情侣的…

能替代微软AD的国产化方案,搭建自主可控的身份管理体系

随着国产化替代步伐加速&#xff0c;以及企业出于信息安全建设的需要&#xff0c;越来越多的企业和组织开始考虑将现有的微软 Active Directory&#xff08;AD&#xff09;替换为国产化的LDAP身份目录服务&#xff08;也称统一身份认证和管理&#xff09;系统。本文将介绍一种国…

世界坐标系转换为平面地图坐标

将世界坐标系转换为平面地图坐标的方法通常涉及地图投影。地图投影是一种将地球(一个三维球体)上的点转换为平面(二维)地图上的点的方法。 这里介绍几种常见的地图投影方法: 墨卡托投影(Mercator Projection): 这是最常见的投影方式之一,尤其用于航海地图。它将经纬度…

2023年春秋杯网络安全联赛冬季赛_做题记录

可信计算 基于挑战码的双向认证1 可信计算赛题-双向认证挑战模式.docx 使用命令进行SSH登录上去 ssh player8.147.131.156 -p 18341 # 记得加上-p参数指定端口&#xff0c;不然默认的是22端口看见word文档的提示&#xff0c;先尝试一下 直接获得了flag1 web 魔术方…

24小时涨粉10w+的AI小游戏-哄哄模拟器

近年来&#xff0c;随着chatGPT的爆火&#xff0c;一系列的AI应用应运而生。比如&#xff1a;AI绘画&#xff0c;AI写作等。今天我们来看看最近很火的一个AI小游戏-哄哄模拟器。 1. 试玩体验 这款游戏名叫“哄哄模拟器”&#xff0c;体验地址为&#xff1a;https://hong.grea…

贪吃蛇项目

引言&#xff1a; 本文章使用C语言在Windows环境的控制台中模拟实现经典小游戏贪吃蛇。 实现基本功能&#xff1a; 1.贪吃蛇地图绘制。 2.蛇吃食物的功能&#xff08;上、下、左、右方向键控制蛇的动作&#xff09; 3.蛇撞墙死亡 4.蛇咬到自己死亡 5.计算得分 6.蛇加速…

回文子字符串的个数

判断一个字符串是否是一个回文除了从两端向里移动指针&#xff0c;也可以采用指针从字符串中心开始向两端延伸。即如果存在一个长度为m的回文子字符串&#xff0c;再分别向该回文两端延伸一个字符&#xff0c;并判断这两个字符是否相同&#xff0c;如果相同则找到了一个长度为m…

PMP备考笔记:模拟考试知识点总结

1. 答题思路&#xff1a;优先看问题&#xff0c;可节省时间。 2. 考试就按照考试的套路来做&#xff0c;不要过多考虑。 开发团队只专注当前冲刺目标&#xff0c;产品负责人对PB排优先级。 收集需求工具-原型法&#xff1a;能够让用户提前体验&#xff0c;减少返工的风险。 …

centos7上安装mysql5.7并自定义数据目录路径

1、卸载mariadb rpm -qa |grep mariadb #查出来的结果是mariadb-libs-5.5.68-1.el7.x86_64 rpm -e mariadb-libs-5.5.68-1.el7.x86_64 --nodeps #卸载查到的结果 2、官网下载响应的tar.gz包&#xff0c;比如mysql-5.7.38-el7-x86_64.tar.gz &…

线段树分治总结

线段树分治总结 概念例题二分图 /【模板】线段树分治[HAOI2017] 八纵八横[FJOI2015] 火星商店问题EnvyExtending Set of PointsForced Online Queries Problem「雅礼集训 2018 Day10」贪玩蓝月BZOJ4184-shallot[bzoj4644]经典**题 概念 \qquad 线段树分治一般用来解决带有如下两…

MyBatis 的XML实现方法

MyBatis 的XML实现方法 MyBatis 的XML实现方法前情提示创建mapper接口添加配置创建xml文件操作数据库insert标签delete标签select标签resultMap标签 update标签sql标签,include标签 MyBatis 的XML实现方法 前情提示 关于mybatis的重要准备工作,请看MyBatis 的注解实现方法 创…

骨传导耳机对身体有什么危害?危害严重吗

骨传导耳机虽然提供了一种避免直接将声音传输至耳道的新方式&#xff0c;减少了对耳道和鼓膜的潜在损害&#xff0c;但它们也并非完全没有潜在的危害性。尽管存在潜在的注意点&#xff0c;但相比于传统的入耳式耳机&#xff0c;骨传导耳机普遍对听力的影响较小。以下是一些骨传…

排除WLAN故障

排除网络故障 第 1步&#xff1a;测试连接 第2步&#xff1a;调查问题的原因并且记录自己发现的问题 1. 无线路由器IP地址错误&#xff0c;不在同一个网段 2. Home PC 应该要和Home wireless连接 3. table pc应设为DHCP 4. 测试url www.netacad.pt ,发现无法连通&#xf…

echarts条形图添加滚动条

效果展示: 测试数据: taskList:[{majorDeptName:测试,finishCount:54,notFinishCount:21}, {majorDeptName:测试,finishCount:54,notFinishCount:21}, {majorDeptName:测试,finishCount:54,notFinishCount:21}, {majorDeptName:测试,finishCount:54,notFinishCount:21}, {maj…

机器学习 | 掌握 K-近邻算法 的理论实现和调优技巧

目录 初识K-近邻算法 距离度量 K值选择 kd树 数据集划分 特征预处理 莺尾花种类预测(实操) 交叉验证与网格搜索 初识K-近邻算法 K-近邻算法&#xff08;K-Nearest Neighbor&#xff0c;KNN&#xff09;是一种基本的分类和回归算法。它的基本思想是通过找出与新对象最近…

PhpStorm调试docker容器中的php项目

背景 已经通过docker容器启动了一个web服务&#xff0c;并在宿主机可以访问http://localhost:8080访问网页。 现在想使用phpstorm打断点调试代码。 方法 1. 容器内安装xdebug 进入容器 docker exec -it <container-name> bash为php安装xdebug拓展 apt install php8…

TypeScript(六) 循环语句

1. TypeScript循环语句 1.1. 简述 有的时候&#xff0c;我们可能需要多次执行同一块代码。一般情况下&#xff0c;语句是按顺序执行的&#xff1a;函数中的第一个语句先执行&#xff0c;接着是第二个语句&#xff0c;依此类推。   循环语句允许我们多次执行一个语句或语句组…

C#需要学到什么程度才能做MES系统开发工作?

C#需要学到什么程度才能做MES系统开发工作&#xff1f; 在开始前我分享下我的经历&#xff0c;刚入行时遇到一个好公司和师父&#xff0c;给了我机会&#xff0c;两年时间从3k薪资涨到18k的&#xff0c; 我师父给了一些C#学习方法和资料&#xff0c;让我不断提升自己&#xff…

GNSS定位技术总结与PPP定位技术

1.统一观测值方程 2.PPP方程构建 站间单差方程如下&#xff1a; 同样的&#xff0c;设计矩阵也更加庞大&#xff1a; 站间单差消除了卫星轨道、卫星钟、电离层、对流层以及卫星端的伪距和载波硬件延迟的影响。但在PPP中&#xff0c;我们无法通过站间单差消除这些影响&#xff…