Kotlin之控制语句和表达式

原文链接 Kotlin Controls and Expressions

有结果返回的是表达式,没有返回的称之为语句,语句最大的问题是它没有返回值,那么想要保存结果就必然会产生副作用,比如改变变量。很多时候这是不够方便的,并且在多线程条件下,这甚至是不安全的。Kotlin中,为了加强线程安全性和方便并发和异步,因此绝大多数语句都是表达式。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

分支表达式

Kotlin中没有三元条件符(a > b ? a : b),但它的条件分支都是表达式,可以直接放在赋值符的右边,或者用在return语句中。

if表达式

它是一个两个分支的表达式,是有返回值的:

val maxV = if (a > b) a else b

当然了,把它当作常规的语句来使用也是没有问题的:

var max: Int
if (a > b) {max = a
} else {max = b
}

when表达式

当超过2个分支时,if就不能用了,这时可以用when表达式,它支持多个分支,类似于其他语言中的switch:

when (x) {1 -> println("it is 1")2 -> println("it is 2")else -> {println("it is neight 1 nor 2")}
}

需要注意的是,每一行是一个条件,并不是单单指参数与其相等,比如:

when (x) {in 1..5 -> println("Less than 5 bigger than 1")x.isEven() -> println("it is even")else -> println("It is neither even or less than 5")
}

当然,最重要的是when是一个表达式,可以直接用在赋值符的右边,或者当参数传,或者用在return中

fun Request.getBody() =when (val response = executeRequest()) {is Success -> response.bodyis HttpError -> throw HttpException(response.status)}

这里的when就是函数的返回值,可以看到when是一个表达式,它会返回一个值,这个值直接作为函数的返回值

从这几个示例可以看出when表达式相当强大比其他语言的switch要强大许多,并且可以直接当作返回值,当需要超过2个条件分支时就可以使用when表达式。

循环语句

循环是语句,与其他语言也差不多。

while loop

while (x < 10) {println(x)x++
}

屁股向后式do-while loop

do {x = poll()
} while (x < 10)

强大的for loop

这个是最强大,也是最常用的循环语句遍历数组,集合和固定步长时的首选。

for (item in collection) print(item)

这里的collection可以是数组和集合(列表和Set)。严格来说只要collection类型实现了iterator()和next(),就可以在for loop中使用。

for加上range,可以非常强大:

for (i in 1..10) // = for (int i = 1; i <= 10; i++)
for (i in 0 until 10) // = for (int i = 0; i < 10; i++)
for (i in 9 downTo 0) // = for (int i = 9; i >= 0; i--)
for (i in 0 until 10 step 2) // = for (int i = 0; i < 10; i += 2)

如果是数组或者列表,但又必须要用索引,也可以直接来:

for (i in array.indices) {println(array[i]) // 'i' is the index
}

其实有更好的方式:

for ((index, value) in array.withIndex()) {println("the element at $index is $value")
}

其实吧,Kotlin是多范式编程语言,天生支持函数式编程,多数情况下不建议直接上for loop,而是用函数式方式的forEach,数组和集合都支持forEach的:

array.forEach { println(it) }

终止语句

当想提前退出函数的执行,或者循环时,就需要用到终止语句,有三种return, break和continue

return终止函数执行

这个都比较熟悉,常规的用法都是一样的,可以提前退出函数:

fun plot(x: Int) {if (x < 1) {return -1}...return y
}

但当有嵌套的lambda时,如不特别指定,return会退出外层的函数,而不是像想当然的退出lambda,比如:

fun foo() {listOf(1, 2, 3, 4, 5).forEach {if (it == 3) return // non-local return directly to the caller of foo()print(it)}println("this point is unreachable")
}

这个不是终止lambda的执行,而是直接退出函数foo的执行。如果想解决呢,即也退出遍历的lambda有三种方案:

  • 使用标签
fun foo() {listOf(1, 2, 3, 4, 5).forEach lit@{if (it == 3) return@lit // local return to the caller of the lambda - the forEach loopprint(it)}print(" done with explicit label")
}
  • 使用隐式标签,也即遍历的方法当作标签
fun foo() {listOf(1, 2, 3, 4, 5).forEach {if (it == 3) return@forEach // local return to the caller of the lambda - the forEach loopprint(it)}print(" done with implicit label")
}
  • 使用匿名函数而不是lambda,匿名函数与常规函数体效力一样,所以return只在函数体内生效
fun foo() {listOf(1, 2, 3, 4, 5).forEach(fun(value: Int) {if (value == 3) return  // local return to the caller of the anonymous function - the forEach loopprint(value)})print(" done with anonymous function")
}

这三种方式,如果非要使用,建议使用方式二,用自带的隐式label,因为比较方便,可读性也不差。

但,非常不建议如此使用return语句,这本是应该避免的问题,lambda多半是用在函数式遍历和处理,在lambda里面提加return本就是非常奇怪的事情。因为如果某些条件不满足,想不执行此lambda,应该用filter啊,而不是笨拙的非要在lambda中去终止:

fun foo() {listOf(1, 2, 3, 4, 5).filter(i -> i != 3).forEach { println(it) }print("You can do whatever you like here.")
}

循环的终止

break终止当前循环,continue则是跳过当前循环的当前步骤,直接跳到下一次迭代。这两个的常规使用与其他语言是一样的。

但对于break,一般来说有一个痛点,就是当有循环嵌套时,break只能终止一层,如果想终止所有循环时,只能再手动的加条件去判断,然后再一层一层的break,比如:

for (i in 0 until 10) {var found = falsefor (j in i until 10) {if (array[i] + array[j] == target) {found = truebreak // only break inner for loop}}if (found) {break // this break outer for loop}
}

这多少有点笨拙和丑陋,Kotlin有更优雅的解决方式,就是引入了标签label,可以给循环加上标签,在break时可以指定标签,同样是上面的情况,可以这样做:

loop@ for (i in 0 until 10) {for (j in i until 10) {if (array[i] + array[j] == target) {break @loop // break all loops easily}}
}

其实吧,这玩意儿跟当年的goto是一样的,虽然可行,但不建议多使用标签多了以后会让程序的执行更加的混乱,试想假如在层层循环中break错了某个标签,调试的难度是相当大的。更多的时候需要仔细想想有没有更好的遍历方式,而不是靠标签来救命。

原创不易,打赏点赞在看收藏分享 总要有一个吧

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

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

相关文章

sass 生成辅助色

背景 一个按钮往往有 4 个状态。 默认状态hover鼠标按下禁用状态 为了表示这 4 个状态&#xff0c;需要设置 4 个颜色来提示用户。 按钮类型一般有 5 个&#xff1a; 以 primary 类型按钮为例&#xff0c;设置它不同状态下的颜色&#xff1a; <button class"btn…

[量子计算与量子信息] 2.1 线性代数

2.1 线性代数 符号对照表 量子力学中&#xff0c;向量使用 ∣ ψ ⟩ \ket \psi ∣ψ⟩ (ket)来表示&#xff0c;可以理解为一个列向量。其对偶向量为 ⟨ ψ ∣ \bra \psi ⟨ψ∣ &#xff0c;可以理解为行向量。 向量空间中零向量直接用 0 0 0 表示&#xff0c; ∣ 0 ⟩ \…

Spring后端HttpClient实现微信小程序登录

这是微信官方提供的时序图。我们需要关注的是前后端的交互&#xff0c;以及服务端如何收发网络请求。 小程序端 封装基本网络请求 我们先封装一个基本的网络请求。 const baseUrl"localhost:8080" export default{sendRequsetAsync } /* e url&#xff1a;目标页…

nodejs+vue+python+PHP+微信小程序-安卓-房产中介管理信息系统的设计与实现-计算机毕业设计

目 录 摘 要 I ABSTRACT II 目 录 II 第1章 绪论 1 1.1背景及意义 1 1.2 国内外研究概况 1 1.3 研究的内容 1 第2章 相关技术 3 2.1 nodejs简介 4 2.2 express框架介绍 6 2.4 MySQL数据库 4 第3章 系统分析 5 3.1 需求分析 5 3.2 系统可行性分析 5 3.2.1技术可行性&#xff1a;…

【学习辅助】Axure手机时间管理APP原型,告别手机控番茄任务模板

作品概况 页面数量&#xff1a;共 30 页 兼容软件&#xff1a;Axure RP 9/10&#xff0c;不支持低版本 应用领域&#xff1a;时间管理、系统工具 作品申明&#xff1a;页面内容仅用于功能演示&#xff0c;无实际功能 作品特色 本品为「手机时间管理」APP原型&#xff0c;…

机器视觉系统的组成

图像获取 光学系统采集图像&#xff0c;图像转换成模拟格式并传入计算机存储器。 图像处理和分析 处理器运用不同的算法来提高对结论有重要影响的图像要素并形成数据作为判决依据。 判决和输出 处理器的控制程序根据收到的数据做出结论并输出信息作反馈控制等应用。

EasyPOI实现excel文件导出

EasyPOI真的是一款非常好用的文件导出工具&#xff0c;相较于传统的一行一列的数据导出&#xff0c;这种以实体类绑定生成的方式真的非常方便&#xff0c;也希望大家能够了解、掌握其使用方法&#xff0c;下面就用一个实例来简单介绍一下EasyPOI的使用。 1.导入依赖 <!-- e…

P6入门:项目初始化9-项目详情之资源 Resource

前言 使用项目详细信息查看和编辑有关所选项目的详细信息&#xff0c;在项目创建完成后&#xff0c;初始化项目是一项非常重要的工作&#xff0c;涉及需要设置的内容包括项目名&#xff0c;ID,责任人&#xff0c;日历&#xff0c;预算&#xff0c;资金&#xff0c;分类码等等&…

数据结构 | 队列的实现

数据结构 | 队列的实现 文章目录 数据结构 | 队列的实现队列的概念及结构队列的实现队列的实现头文件&#xff0c;需要实现的接口 Queue.h初始化队列队尾入队列【重点】队头出队列【重点】获取队列头部元素获取队列队尾元素获取队列中有效元素个数检测队列是否为空销毁队列 Que…

ChatGPT 4 OpenAI 数据分析动态可视化案例

数据分析可视化是一种将原始数据转化为图形或图像的方法,使得数据更易理解和解读。这种方法能够帮助我们更清楚地看到数据中的模式、趋势和关联性,从而更好地理解数据,并据此做出决策。 数据分析可视化的一些常见形式包括: 1. 折线图:常用于展示数据随时间的变化趋势。 …

Maya v2024(3D动画制作软件)

Maya 2024是一款三维计算机图形动画制作软件。它被广泛应用于电影、电视、游戏、动画等领域中&#xff0c;用于创建各种三维模型、场景、特效和动画。 以下是Maya的主要特点&#xff1a; 强大的建模工具&#xff1a;Maya提供了各种建模工具&#xff0c;如多边形建模、NURBS建模…

基于XML的声明式事务

场景模拟 参考基于注解的声明式事务 修改Spring的配置文件 将Spring配置文件中去掉tx:annotation-driven标签&#xff0c;并添加配置&#xff1a; <?xml version"1.0" encoding"UTF-8"?> <beans xmlns"http://www.springframework.org…

nacos适配达梦数据库

一、下载源码 源码我直接下载gitee上nacos2.2.3的&#xff0c;具体链接&#xff1a;https://gitee.com/mirrors/Nacos/tree/2.2.3&#xff0c;具体如下图&#xff1a; 二、集成达梦数据库驱动 解压源码包&#xff0c;用idea打开源码&#xff0c;等idea和maven编译完成&#xff…

el-table解决数据过少小于高度有留白的问题

问题:给el-table设置个高度,高度为500px,之后就添加如下4条数据,那么底部就没数据,直接就空白了,本文章就是为了解决这个问题,如果底部留白那么就添加几条空数据就行了.如果数据已达到高度了那么就不会留白了 1.效果 这个空列可以根据高度来决定添加几个空格子去铺满列表&…

P6入门:项目初始化5-项目支出计划Spending Plan

前言 使用项目详细信息查看和编辑有关所选项目的详细信息&#xff0c;在项目创建完成后&#xff0c;初始化项目是一项非常重要的工作&#xff0c;涉及需要设置的内容包括项目名&#xff0c;ID,责任人&#xff0c;日历&#xff0c;预算&#xff0c;资金&#xff0c;分类码等等&…

路由器的结构以及工作原理

目录 路由器的结构 交换结构三种常用的交换方式 1.通过存储器 2.通过总线 3.通过纵横交换结构&#xff08;crossbar switch fabric&#xff09; 路由器的结构 路由器结构可划分为两大部分&#xff1a;路由选择部分&#xff0c;分组转发部分 路由选择部分也叫做控制部分&…

VB.net TCP服务端监听端口接收客户端RFID网络读卡器上传的读卡数据

本 示例使用设备介绍&#xff1a;WIFI/TCP/UDP/HTTP协议RFID液显网络读卡器可二次开发语音播报POE-淘宝网 (taobao.com) Imports System.Threading Imports System.Net Imports System.Net.Sockets Public Class Form1Dim ListenSocket As SocketDim Dict As New Dictionary(Of…

Azure 机器学习:在 Azure 机器学习中使用 Azure OpenAI 模型

目录 一、环境准备二、Azure 机器学习中的 OpenAI 模型是什么&#xff1f;三、在机器学习中访问 Azure OpenAI 模型连接到 Azure OpenAI部署 Azure OpenAI 模型 四、使用自己的训练数据微调 Azure OpenAI 模型使用工作室微调微调设置训练数据自定义微调参数部署微调的模型 使用…

基于python+TensorFlow+Django卷积网络算法+深度学习模型+蔬菜识别系统

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介 二、功能三、系统四. 总结 一项目简介 介绍了TensorFlow在图像识别分类中的应用&#xff0c;并通过相关代码进行了讲解。通过TensorFlow提供的工具和库&am…

Linux学习第41天:Linux SPI 驱动实验(二):乾坤大挪移

Linux版本号4.1.15 芯片I.MX6ULL 大叔学Linux 品人间百味 思文短情长 本章的思维导图如下&#xff1a; 二、I.MX6U SPI主机驱动分析 主机驱动一般都是由SOC厂商写好的。不作为重点需要掌握的内容。 三、SPI设备驱动编写流程 1、SP…