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 协议号这样一个五…

路口最短时间问题(100%用例)C卷(JavaPythonC++Node.jsC语言)

假定街道是棋盘型的,每格距离相等,车辆通过每格街道需要时间均为 timePerRoad; 街道的街口(交叉点)有交通灯,灯的周期T(=lights[row][col])各不相同;车辆可直行、左转和右转,其中直行和左转需要等相应T时间的交通灯才可通行,右转无需等待。 现给出 n*m 个街口的交通灯周期…

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

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

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

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

ABAP EXCEL 转 PDF

DATA: application TYPE ole2_object, workbook TYPE ole2_object, sheet TYPE ole2_object. IF iv_pdf IS NOT INITIAL. CREATE OBJECT application ‘EXCEL.APPLICATION’. CALL METHOD OF application ‘WORKBOOKS’ workbook. CALL METHOD OF workbook ‘OPEN’ EXPORTING…

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

将世界坐标系转换为平面地图坐标的方法通常涉及地图投影。地图投影是一种将地球(一个三维球体)上的点转换为平面(二维)地图上的点的方法。 这里介绍几种常见的地图投影方法: 墨卡托投影(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 线段树分治一般用来解决带有如下两…

Spring 学习2 --基于xml管理Bean

1、xml管理Bean <?xml version"1.0" encoding"UTF-8"?> <beans xmlns"http://www.springframework.org/schema/beans"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://www.springfr…

MyBatis 的XML实现方法

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

systemd:service与target使用及相关命令

文章目录 一、 unit1.1 unit常用命令 二、 service系统服务2.1 unit service配置文件2.1.1 [Unit]区块2.1.2 [Service]区块2.1.3 [Install]区块2.1.4 示例介绍 2.2 service常用命令 三、target3.1 tartget有关命令 四、其他系统命令4.1 systemctl 相关系统管理命令4.2 journalc…

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

骨传导耳机虽然提供了一种避免直接将声音传输至耳道的新方式&#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…

论文阅读:求解约束多目标区间优化的交互多属性决策NSGA-II算法

求解约束多目标区间优化的交互多属性决策NSGA-II算法 作者&#xff1a;陈志旺,陈林,白锌,杨七,赵方亮 期刊&#xff1a;控制与决策、2015.05 DOI&#xff1a;10.13195/j.kzyjc.2014.0455 内容简介 针对约束多目标区间优化问题,提出一种交互多属性决策NSGA-II算法.该算法将非线…

echarts条形图添加滚动条

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