Kotlin开发笔记:集合和逆变协变

Kotlin开发笔记:集合和逆变协变

在这里插入图片描述

Kotlin中的集合

基本的集合类型

Kotlin中的集合类型和Java差不多,不过有些在名称上可能有出入,下面是Kotlin中的一些基本集合类型:

类型介绍
Pair两个值的元组
Triple三个值的元组
Array经过索引的,固定大小的对象和基元集合
List有序的对象集合
Set无序的对象集合
Map键值对对象集合

Kotlin中的视图

在Kotlin引入了视图的概念,简而言之,不同的视图类型会赋予我们对操作集合的不同权限。Kotlin中有两种不同的视图:只读或不可变视图,以及读写或可变视图

比如对于List来说,有两种视图,分别是List和MutableList,前者提供只读视图,后者提供读写视图。当我们用List视图时将无法修改列表,而用MutableList就可以。

    var li = listOf(1,2,3) as MutableListli.add(5)

比如我们运行上述代码就会报错,因为listOf函数会产生List视图的集合,这将导致我们无法修改列表,如果我们要续写就需要产生读写视图的列表:

    var li = mutableListOf(1,2,3) li.add(5)

不过本质上这两种视图都是对List的引用,比如说我们可以用List视图引用同一个ArrayList:

    val ar = arrayListOf(1,2,3,4)val li:List<Int> = arval li1:MutableList<Int> = ar

不过List视图的引用将无法修改列表本身。

Kotlin中的一些技巧

使用listOf等函数快速创建集合

这个其实在上面给的例子里已经体现了,我们可以使用arrayListOf,listOf等函数快速创建出我们想要的集合而无需再用构造函数。

使用to和mapOf快速创建表

Kotlin中提供了一个to拓展函数,这个函数将生成一个Pair类型的对象,比如

val p1 = "age" to 18

将会创建一个First为"age",Second为18的Pair对象。而这个对象又可以用于mapOf函数。这样我们就可以快速创建一个map,比如:

val mMap = mapOf("age" to 18,"code" to 10086)

这样就创建了一个键值对为< String , Int >类型的map,其中to之前的为Key,之后的为Value。

同时获取索引和值

在Java中,如果我们想要同时获取一个List的索引和值的话可能需要遍历或者采取别的手段来达到这个目的,而在Kotlin中,我们可以用解构来实现这个目的:

fun main() {val li = listOf("jack","anderson")for((index,value) in li.withIndex()){println("index : $index, value:$value")}
}

withIndex将返回一个包含键值对的对象,我们将其解构出来就可以同时获得索引和值了。

创建有规律的数组

接下来介绍的是如何创建出一个有规律的数字,比如我们可以创建出一个物的倍数的数组:

fun main() {val li = Array(5){index -> index * 5}for(value in li){println(value)}
}

Array括号后面的5是元素个数,index下标是从0开始,我们可以打印出值:
在这里插入图片描述
成功创建了一个包含五的倍数的数组,利用这个技巧我们再加上Array内置的一些方法,就可以实现许多计算,比如我们想要计算从1到5的平方和的话就可以直接这样写:

fun main() {val li = Array(5){index -> (index+1) * (index+1)}.sum()println(li)
}

使用in

在Java中如果我们想要判断一个元素是否在一个集合中,一般会使用contains方法,不过在Kotlin中提供了in运算符实现了同样的效果:

fun main() {val li = Array(5){index -> index*5}println(0 in li)
}

实际上在迭代时我们会用到in运算符也是这样。

Kotlin中的逆变和协变

什么是逆变和协变

首先我们需要介绍逆变和协变的概念,协变和逆变都是术语,前者指能够使用比原始指定的派生类型的派生程度更大(更具体的)的类型,后者指能够使用比原始指定的派生类型的派生程度更小(不太具体的)的类型。

以我的理解,协变应该接近于extend,而逆变接近于super。

默认情况下,在Java中泛型强制实行类型不变性–也就是说,如果泛型函数期望一个参数类型T,则不允许替换基类型T或者派生类型T,类型必须是完全预期的类型

实际上,在Java中我也没有对通配符和一般的泛型T的区别和相同有什么很深的理解。我的理解是,通配符?代表不确定的类型,泛型类型T代表确定的类型

类型不变性

这里再介绍一下类型不变性,当一个方法接收到一个类型为T的对象(确定对象,不是泛型对象)时,我们可以传入为T类型或者是T的子类的对象。比如如果一个方法接收一个Animal类型的对象,那么身为Animal子类的Cat类型的对象也可以被传进去。

但是,如果这个方法接收的是一个泛型类型为T的对象,那么将不允许传递派生类型为T的泛型对象。比如,如果可以传递List< Animal >类型的对象,那么将不允许传入List< Cat >类型的对象,这和Java中的类型擦除有关。

书上的一个例子我觉得很形象,比如说我们创建一个Fruit类和两个继承它的类还有一个接收水果的方法:

open class Fruit
class Orange:Fruit()
class Banana:Fruit()fun receiveFruits(fruits:Array<Fruit>){println("水果的数量是${fruits.size}")
}

这个方法可以接受泛型类型为Fruit的数组,如果我们传入Orange或者Banana会怎么样呢?
在这里插入图片描述
可以看到,编译器提示类型不匹配了。香蕉是从水果继承而来的,但是显然一篮子香蕉不是从一篮子水果继承而来的

不过一旦我们用list视图来操作,上述代码就不会报错了:
在这里插入图片描述
这是因为List视图只允许我们进行读而不允许我们进行写,这样是安全的。在Kotlin中,这个效果是由于List视图是out修饰的,我们将在后面的协变中介绍。
在这里插入图片描述

使用协变

上边介绍到了,一旦我们使用List视图,那么receiveFruits方法就可以被调用了,这正是由于使用了协变的原因。接下来我们创建一个方法来模拟协变的使用场景,比如我们想要把一个Fruit的Array复制到另一个Fruit的Array中:

fun copyFromTo(from:Array<Fruit>,to:Array<Fruit>){for(i in 0 until from.size){to[i] = from[i]}
}

这种情况下我们显然不能传入除Fruit类之外的泛型类,比如:
在这里插入图片描述
编译器是不会允许我们传入泛型类型为Banana的参数给from的,这个时候我们只需要修改一下这个函数,在传入的from参数处使用协变即可:

fun copyFromTo(from:Array<out Fruit>,to:Array<Fruit>){for(i in 0 until from.size){to[i] = from[i]}
}

在这里插入图片描述
这样编译器就不会报错了。要理解这个协变的含义我们可以从编译器为什么不让我们传入Banana类型的参数看。如果我们可以传入Banana类型的参数,我们就有可能对Banana执行一些Fruit层面的指令。

举个例子来说,大部分水果冲洗完成之后就可以直接食用了,但是香蕉的果皮较厚,我们就不能直接食用,在这之前还需要剥皮。身为子类的Banana🍌肯定是有其特殊之处的,不能用基类Fruit的一些操作直接用在Banana上。但是如果我们不对这个Banana进行操作的话,那么就不会有什么大问题了,这就是协变的含义。

这里对copyFromTo方法的from参数加上out参数后就说明我们不会对这个from参数进行任何方法的调用了,我们只是单单读取这个参数,这样编译器就允许我们传入Fruit的子类的泛型类型了,换言之,我们就实现了协变。这种在使用泛型类型时使用协变的行为称之为“使用点型变”。

使用逆变

与协变相对的就是逆变了,如果说协变是只读不写的话,那么逆变就是只写不读。实际上也确实是这样,使用逆变将允许我们在该参数上进行设置值的方法调用,而不允许读取的方法。

我们依旧以上面的copyFromTo方法为例,现在我们希望可以将任意Fruit或者Fruit子类的元素复制到Fruit或Fruit超类的集合中,比如说我们传一个Any类的参数:
在这里插入图片描述
显然由于类型不变性这样是行不通的,在这里我们再次对copyFromTo方法做修改,这次我们对to参数使用逆变:

fun copyFromTo(from:Array<out Fruit>,to:Array<in Fruit>){for(i in 0 until from.size){to[i] = from[i]}
}

在这里插入图片描述
这样编译器就允许我们这样调用了。

使用Where的参数类型约束

这部分内容说白了就是约束泛型类型的范围,比如说我们有一个方法需要传入一个泛型类,这个泛型类需要实现AutoCloseable接口,那么我们就可以这样写

fun <T:AutoCloseable> useAndClose(input:T)
{input.close()
}

实际上上面的和Java中的写法也差不多,不过如果是一个泛型需要实现多个接口的话就不能这么写了,需要我们用where参数进行约束:

fun <T> useAndClose(input:T)where T:AutoCloseable,T:Appendable
{input.append("haha")input.close()
}

where约束跟在参数列表后面,花括号前面。约束参数中用逗号分隔。

星投影

星投影用<*>定义参数类型,它是指定泛型只读类型和原始类型的Kotlin等效物,**简单来说,我们可以用星投影捕获泛型类型,但是我们只能对捕获的泛型类型进行读取而不能修改。**当你想表达对类型不太了解但有希望类型安全时,请使用星投影,星投影只允许读出而不允许写入,比如:

fun printValues(values:Array<*>){for(value in values){println(value)}
}

在这个方法中我们用星投影捕获了泛型类型,但是我们只能读取values值不能写入或者更改values值,实际上就相当于out T,但是写起来更简洁。

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

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

相关文章

去掉鼠标系列之一: 语雀快捷键使用指南

其实应该是系列之二了&#xff0c;因为前面写了一个关于Interlij IDEA的快捷键了。 为什么要写这个了&#xff0c;主要是觉得一会儿用鼠标&#xff0c;一会儿键盘&#xff0c;一点儿不酷&#xff0c;我希望可以一直用键盘&#xff0c;抛开鼠标。后面陆续记录一下各个软件的快捷…

高效使用ChatGPT之ChatGPT客户端

ChatGPT客户端&#xff0c;支持Mac, Windows, and Linux 下载地址见文章结尾 软件截图 Windows: Mac&#xff1a; 说明 chatgpt桌面版&#xff0c;相比于网页版的chatgpt&#xff0c;最大的特色是支持历史聊天对话记录导出&#xff0c;且支持三种格式&#xff1a;PNG、PDF、…

由浅入深详解四种分布式锁

在多线程环境下&#xff0c;为了保证数据的线程安全&#xff0c;锁保证同一时刻&#xff0c;只有一个可以访问和更新共享数据。在单机系统我们可以使用synchronized锁或者Lock锁保证线程安全。synchronized锁是Java提供的一种内置锁&#xff0c;在单个JVM进程中提供线程之间的锁…

小程序的数据绑定和事件绑定

小程序的数据绑定 1.需要渲染的数据放在index.js中的data里 Page({data: {info:HELLO WORLD,imgSrc:/images/1.jpg,randomNum:Math.random()*10,randomNum1:Math.random().toFixed(2)}, }) 2.在WXML中通过{{}}获取数据 <view>{{info}}</view><image src"{{…

RocketMQ 5.0 架构解析:如何基于云原生架构支撑多元化场景

作者&#xff1a;隆基 本文将从技术角度了解 RocketMQ 的云原生架构&#xff0c;了解 RocketMQ 如何基于一套统一的架构支撑多元化的场景。 文章主要包含三部分内容。首先介绍 RocketMQ 5.0 的核心概念和架构概览&#xff1b;然后从集群角度出发&#xff0c;从宏观视角学习 R…

优测云服务平台|【压力测试功能升级】轻松完成压测任务

一、本次升级主要功能如下&#xff1a; 1.多份报告对比查看测试结果 2.报告新增多种下载格式 Word格式Excel格式 3.新增多种编排复杂场景的控制器 漏斗控制器并行控制器事务控制器仅一次控制器分组控制器集合点 4.新增概览页面&#xff0c;包含多种统计维度 二、报告对比…

开源语音聊天软件Mumble

网友 大气 告诉我&#xff0c;Openblocks在国内还有个版本叫 码匠&#xff0c;更贴合国内软件开发的需求&#xff0c;如接入了国内常用的身份认证&#xff0c;接入了国内的数据库和云服务&#xff0c;也对小程序、企微 sdk 等场景做了适配。 在 https://majiang.co/docs/docke…

类与对象(上)

类与对象&#xff08;上&#xff09; 一、面向过程和面向对象的区别二、类1、类的引入2、类的定义&#xff08;1&#xff09;类的基本定义&#xff08;2&#xff09;类的成员函数的定义方法 3、类的访问限定符4、封装5、驼峰法命名规则6、类的作用域7、类的实例化&#xff08;1…

金蝶软件实现导入Excel数据分录行信息到单据体分录行中

>>>适合KIS云专业版V16.0|KIS云旗舰版V7.0|K/3 WISE 14.0等版本<<< 金蝶软件中实现[导入Excel数据业务分录行]信息到[金蝶单据体分录]中,在采购订单|采购入库单|销售订单|销售出库单等类型单据中,以少量的必要字段在excel表格中按模板填列好,很方便快捷地从…

IntelliJ IDEA(简称Idea) 基本常用设置及Maven部署---详细介绍

一&#xff0c;Idea是什么&#xff1f; 前言&#xff1a; 众所周知&#xff0c;现在有许多编译工具&#xff0c;如eclipse&#xff0c;pathon, 今天所要学的Idea编译工具 Idea是JetBrains公司开发的一款强大的集成开发环境&#xff08;IDE&#xff09;&#xff0c;主要用于Java…

Rancher管理K8S

1 介绍 Rancher是一个开源的企业级多集群Kubernetes管理平台&#xff0c;实现了Kubernetes集群在混合云本地数据中心的集中部署与管理&#xff0c;以确保集群的安全性&#xff0c;加速企业数字化转型。Rancher 1.0版本在2016年就已发布&#xff0c;时至今日&#xff0c;Ranche…

CS:GO升级 Linux不再是“法外之地”

在前天的VAC大规模封禁中&#xff0c;有不少Linux平台的作弊玩家也迎来了“迟到”的VAC封禁。   一直以来&#xff0c;Linux就是VAC封禁的法外之地。虽然大部分玩家都使用Windows平台进行游戏。但实际上&#xff0c;使用Linux畅玩CS:GO的玩家也不在少数。 以前V社主要打击W…

06-微信小程序-注册程序-场景值

06-微信小程序-注册程序 文章目录 注册小程序参数 Object object案例代码 场景值场景值作用场景值列表案例代码 注册小程序 每个小程序都需要在 app.js 中调用 App 方法注册小程序实例&#xff0c;绑定生命周期回调函数、错误监听和页面不存在监听函数等。 详细的参数含义和使…

【LeetCode】543.二叉树的直径

题目 给你一棵二叉树的根节点&#xff0c;返回该树的 直径 。 二叉树的 直径 是指树中任意两个节点之间最长路径的 长度 。这条路径可能经过也可能不经过根节点 root 。 两节点之间路径的 长度 由它们之间边数表示。 示例 1&#xff1a; 输入&#xff1a;root [1,2,3,4,5]…

高并发内存池(centralcache)[2]

Central cache threadcache是每个线程独享&#xff0c;而centralcache是多线程共享&#xff0c;需要加锁&#xff08;桶锁&#xff09;一个桶一个锁 解决外碎片问题&#xff1a;内碎片&#xff1a;申请大小超过实际大小&#xff1b;外碎片&#xff1a;空间碎片不连续&#x…

跨境电商ERP源码大揭秘,让你少走弯路

本文将深入介绍跨境电商ERP源码的重要性以及如何选择和应用它们&#xff0c;让你的电商业务更高效、顺畅。 跨境电商ERP源码的重要性 提升管理效率 跨境电商运营面临着众多挑战&#xff0c;如订单管理、库存追踪和财务报告等。跨境电商ERP源码能够集成这些功能&#xff0c;帮…

自动驾驶,一次道阻且长的远征|数据猿直播干货分享

‍数据智能产业创新服务媒体 ——聚焦数智 改变商业 在6月的世界人工智能大会上&#xff0c;马斯克在致辞中宣称&#xff0c;到2023年底&#xff0c;特斯拉便可实现L4级或L5级的完全自动驾驶&#xff08;FSD&#xff09;。两个月之后&#xff0c;马斯克又在X社交平台上发言&am…

java面试强基(16)

目录 clone方法的保护机制 Java中由SubString方法是否会引起内存泄漏&#xff1f; Java中提供了哪两种用于多态的机制? 程序计数器(线程私有) 如何判断对象是否是垃圾&#xff1f; clone方法的保护机制 clone0方法的保护机制在Object中是被声明为 protected的。以User…

1000元到3000元预算的阿里云服务器配置大全

1000元、2000元或3000元预算能够买的阿里云服务器配置有哪些&#xff1f;可以选择ECS通用算力型u1云服务器、ECS计算型c7或通用型g7实例规格&#xff0c;当然&#xff0c;如果选择轻量应用服务器的话&#xff0c;更省钱&#xff0c;阿里云百科分享1000-3000元预算能买的阿里云服…