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.body
        is 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 loop
        print(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 loop
        print(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 loop
        print(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 = false
   for (j in i until 10) {
       if (array[i] + array[j] == target) {
           found = true
           break // 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错了某个标签,调试的难度是相当大的。更多的时候需要仔细想想有没有更好的遍历方式,而不是靠标签来救命。

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于springboot实现社区疫情防控平台管理系统项目【项目源码】

基于springboot实现社区疫情防控平台管理系统演示 SpringBoot框架 SpringBoot是一个全新开源的轻量级框架。基于Spring4.0设计&#xff0c;其不仅继承了Spring框架原来有的优秀特性&#xff0c;而且还通过简化配置文件来进一步简化了Spring应用的整个搭建以及开发过程。另外在…

语言大模型的预训练过程——从无尽数据到海量知识

从目前研究结果看&#xff0c;Transformer是足够强大的特征抽取器&#xff0c;通过预训练过程&#xff0c;Transformer学到了什么&#xff1f;知识是如何存取的&#xff1f;我们又如何修正错误知识&#xff1f;本文讲述这方面的研究进展。 1、求知&#xff1a;LLM学到了什么知…

【代码随想录】算法训练计划20

1、654. 最大二叉树 题目&#xff1a; 给定一个不重复的整数数组 nums 。 最大二叉树 可以用下面的算法从 nums 递归地构建: 创建一个根节点&#xff0c;其值为 nums 中的最大值。 递归地在最大值 左边 的 子数组前缀上 构建左子树。 递归地在最大值 右边 的 子数组后缀上 构建…

YOLO目标检测——番茄数据集下载分享【含对应voc、coco和yolo三种格式标签】

实际项目应用&#xff1a;番茄检测数据集说明&#xff1a;番茄目标检测数据集&#xff0c;真实场景的高质量图片数据&#xff0c;数据场景丰富标签说明&#xff1a;使用lableimg标注软件标注&#xff0c;标注框质量高&#xff0c;含voc(xml)、coco(json)和yolo(txt)三种格式标签…

CMake中的字符串操作

如果使用set进行字符串拼接&#xff0c;对应的命令格式如下&#xff1a; set(变量名1 ${变量名1} ${变量名2} ...) 关于上面的命令其实就是将从第二个参数开始往后所有的字符串进行拼接&#xff0c;最后将结果存储到第一个参数中&#xff0c;如果第一个参数中原来有数据会对原…

机器自主程度越高人机融合可能会越难

智能算法是指利用人工智能技术来处理和解决问题的算法。这些算法通过模仿人类的思维和学习过程&#xff0c;可以自动从数据中提取知识、推断和决策。尽管智能算法的智能程度可能存在不同的层次&#xff0c;但它们都具有一定的智能特征&#xff0c;并能够完成一些复杂的任务。如…

如何应对网站攻击?F5聚焦网站安全防护

有关数据表明&#xff0c;2023以来全球累计超过1400多万个网站遭受了超过10亿次的网络攻击&#xff0c;几乎每个网站都面临风险。无论是电商、论坛还是官方的平台都不可避免遭遇攻击&#xff0c;黑客则利用网络服务的中断期间从事窃取信息等非法活动。在多种攻击手段中&#xf…

计算机基础知识50

数据的增删改查(insert update delete select) # 用户列表的展示&#xff1a; # 把数据表中得用户数据都给查询出来展示在页面上 1. 查询 from app01 import models models.UserInfo.objects.all() # 查询所有的字段信息和数据 resmodels.UserInfo.objects.first() # 查询…

Arcgis打开报错error code=-15

Provide your license server administrator with the following information: Error Code -15 问题描述 原因 长时间闲置后&#xff0c;license server administrator会关闭服务。再次打开之后会出现这个报错 解决方案 重启或者按下述做法&#xff1a; 打开任务管理器&am…

一文2000字从0到1使用压测神器JMeter进行压力测试!

概 述 Apache JMeter 是 Apache组织开发的基于 Java的压力测试工具。用于对软件做压力测试&#xff0c;它最初被设计用于 Web应用测试但后来扩展到其他测试领域。它可以用于测试静态和动态资源例如静态文件、Java 小服务程序、CGI 脚本、Java 对象、数据库&#xff0c; FTP 服…

CC1310F128RSMR Sub-1GHz超低功耗无线微控制器芯片

CC1310F128RSMR QFN-32 Sub-1GHz超低功耗无线微控制器 CC1310F128RSMR是一款低成本、 超低功耗、Sub-1 GHz射频器件&#xff0c;它是Simplel ink微控制器(MCU)平台的一部分。该平台由Wi- Fi组成、蓝牙低功耗&#xff0c;Sub-1 GHz&#xff0c;以太网&#xff0c;Zigbee线程和主…

【诺依管理系统-vue3】组件没有重新请求问题

解决方法&#xff1a;destroy-on-close&#xff0c;如图

消息队列中的事务是什么呢?

消息队列中的事务是什么呢&#xff1f; 说到事务&#xff0c;肯定会优先想到数据库中的事务。在数据库中需要事务&#xff0c;是为了保证数据的一致性、完整性、持久性和隔离性。它可以将数据库中的一组操作合并为一个不可分割的工作单元&#xff0c;要么全部执行成功&#xf…

No191.精选前端面试题,享受每天的挑战和学习

🤍 前端开发工程师(主业)、技术博主(副业)、已过CET6 🍨 阿珊和她的猫_CSDN个人主页 🕠 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 🍚 蓝桥云课签约作者、已在蓝桥云课上架的前后端实战课程《Vue.js 和 Egg.js 开发企业级健康管理项目》、《带你从入…

千年密码新解读,DeepMind 开发 Ithaca 破译希腊铭文

铭文、碑刻是过去文明的思想、文化和语言的体现。金石学家破译千年前的密码&#xff0c;需要完成文本修复、时间归因和地域归因三大任务。 主流的研究方式是「字符串匹配」&#xff0c;即凭借记忆或查询语料库匹配字型相似的铭文&#xff0c;这导致了结果的混淆和误判。 为此…

酷柚易汛ERP - 其他出库单操作指南

1、应用场景 处理其他非销售类型的出库单据&#xff0c;比如内部领用福利、赔偿、借出、领用材料、以货抵债等不参与销售管理的出库类业务。 2、主要操作 2.1 新增其他出库单 打开【仓库】-【其他出库单】&#xff0c;新增单据 出库单位成本及出库成本不能录入&#xff1b;…

msvcr71.dll丢失多种解决方法解析,全方位解读msvcr71.dll文件

在日常使用电脑时&#xff0c;你是否曾遇到过“msvcr71.dll文件丢失”的错误提示&#xff1f;别着急&#xff0c;本文将为你详细介绍msvcr71.dll丢失的解决方法&#xff0c;让你迅速解决这一烦恼。 一.多种msvcr71.dll丢失解决方法 修复方法一:重新安装相应软件 首先&#xf…

推荐一份适合所有人做的副业,尤其是程序员。

我建议每个人都去尝试一下网上接单&#xff0c;这是一个门槛低、类型多样的方式&#xff0c;尤其适合程序员&#xff01; 在接单平台上&#xff0c;你可以看到各种类型的兼职。以freelancer为例&#xff0c;你可以在这里找到技术、设计、写作等类型的兼职&#xff0c;只要发挥…

Linux SSH免密登录

目录 简介 创建Linux用户和用户组 配置LINUX静态IP 编辑IP映射 SSH免密登录配置 登录测试 简介 SSH&#xff08;Secure shell&#xff09;是可以在应用程序中提供安全通信的一个协议&#xff0c;通过SSH可以安全地进行网络数据传输&#xff0c;它的主要原理是利用非对称加密…

常见光模块的封装有哪些呢?

光模块的封装&#xff0c;保障了光通信的稳定和可靠性。本文介绍几种常见的光模块的封装类型。 19封装–焊接型光模块&#xff0c;一般速度不高于千兆&#xff0c;多采用SC接口。 SFP封装&#xff1a;SFP&#xff08;Small form-factor pluggable&#xff09;意思是小型可拔插…