Kotlin控制流程

news/2025/2/6 20:08:29/文章来源:https://www.cnblogs.com/ZJHqs/p/18701615

条件与循环

if表达式

Kotlin中的if与Java中的if大致上都差不多,但是Kotlin中没有三元运算符(A ? B : C),可以用if表达式作为代替,例如:

Java
int a = int a = System.currentTimeMillis() % 2 == 1L ? 1 : 0;
Kotlin
val a = if (System.currentTimeMillis() % 2 == 1L) 1 else 0

在我们日常的开发过程中,有些场景使用三元运算符会方便不少,但也带来了一个(个人感觉的)弊端,有时候代码不够清晰,尤其是在需要换行的时候,那我是应该

int a = System.currentTimeMillis() % 2 == 1L ?function1() : function2()

还是

int a = System.currentTimeMillis() % 2 == 1L ? funcation1() :function2()

与之相比Kotlin就会显得更好阅读一些:

val a = if (System.currentTimeMillis() % 2 == 1L) function1()else function2()

when表达式

  • 如何使用when代替switch

when表达式相比于Java中的switch,要强大很多。首先先来看看如何代替switch

switch (a) {case 0:// TODObreak;case 1:// TODObreak;default: // TODObreak;
}
when (a) {0 -> TODO()1 -> TODO()else -> TODO()
}

相比于switch需要经常写break,否则它会不小心执行到我们不需要执行的代码。相比之下when则不需要,可以更好地避免错误的发生

  • when与枚举类/密封类使用
enum class Bit {ZERO, ONE
}

此时如果我们这样使用的话,编译器会告诉我们:'when' expression must be exhaustive, add necessary 'ONE' branch or 'else' branch instead

fun main() {val number = when (getRandomBit()) {Bit.ZERO -> 0}
}private fun getRandomBit(): Bit {TODO()
}

此时补充一下ONE,可以发现我们就不需要写else了,在枚举时还是挺有用的,可以保证你把所有的枚举值都做了一遍处理,防止遗漏

val number = when (getRandomBit()) {Bit.ZERO -> 0Bit.ONE -> 1
}
  • when中使用任意表达式

从上面的例子中可以看到,我们传入的是一个表达式_getRandomBit_(),而在switch中,只能传入int,char,String和enum。不仅是入参可以用任意表达式,在条件分支中也可以,例如:

when (x) {s.toInt() -> print("s encodes x")else -> print("s does not encode x")
}
// 检查一个值是否在一个区间或集合中(在 -- in, 不在 -- !in)
when (x) {in 1..10 -> print("x is in the range")in validNumbers -> print("x is valid")!in 10..20 -> print("x is outside the range")else -> print("none of the above")
}
// 检查是否是某种特定类型,并且根据特定类型执行对应类型的方法,无需做显示地转换
fun hasPrefix(x: Any) = when(x) {is String -> x.startsWith("prefix")else -> false
}
  • 使用when代替if - else if

这样写好像看起来有点多余,毕竟我们还是更熟悉if - else if

when {x.isOdd() -> print("x is odd")y.isEven() -> print("y is even")else -> print("x+y is odd")
}

但如果我们用于对变量的赋值的话,会发现还是很好用的,比如说:

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

when只有在作为一个表达式的时候(即需要返回一个值),编译器才会强制要求覆盖所有可能得情况。如

fun main() { val currentTime = getCurrentTime() val result = when (currentTime % 3) { 1L -> "1" 0L -> "0" } }

会报错'when' expression must be exhaustive, add necessary 'else' branch

在作为一个语句时,不会强制检查,但为了保持代码的健壮性,最好补充else分支以处理所有的情况

For 循环

  • 遍历提供迭代器的对象
// Kotlin
val list = listOf(1, 2, 3, 4)
for (i in list) {println(i)
}
// Java
ArrayList<Integer> list = new ArrayList<>();
for (int i = 0; i < 4; i++) {list.add(i);
}
for (int i : list) {System.out.println(i);
}
  • 区间与数列

就个人感觉而言,Kotlin的for循环远不如Java的for循环....使用起来太费劲了

不服的可以做下这两道题,其实实现起来很简单,但是用上Kotlin,就老费劲了,不过作为Kotlin的练习也不错

螺旋矩阵

螺旋矩阵 II

升序

// x in a..b 等同于 a <= x <= b
for (i in 1..3) {println(i)
}
// 输出 1 2 3 (换行被我省略了)// x in a until b 等同于 a <= x < b
for (i in 0 until 3) {println(i)
}
// 输出0 1 2 (换行被我省略了)// step x 表示每一次循环x变化的值
for (i in 1..7 step 2) {println(i)
}
// 输出1 3 5 7 (换行被我省略了)

降序

// x in a downTo b 等同于 a >= x >= b
for (i in 3 downTo 1) {println(i)
}
// 输出 3 2 1 (换行被我省略了)
// 恶心的来了,如果我们想达到 a>= x > b的效果,那我们只能:
// x in a downTo (b + 1)

while/do while循环

可以说跟Java没有区别,这里跳过

返回与跳转

  • return 默认从最直接包围它的函数或者匿名函数返回。

  • break 终止最直接包围它的循环。

  • continue 继续下一次最直接包围它的循环。

标签

在Kotlin中,任何表达式都可以用标签标识,标签的格式为标签名称 + 符号@,例如"loop@"

loop1@for (i in 0..3) {loop2@for(j in 0 .. 4) {println(i * j)}
}

标识了之后我们就可以使用break, continue, return来进行跳转了

Break

这个例子中跟我们平时在java中用得完全一样

loop1@for (i in 0..3) {loop2@for(j in 0 .. 4) {if (j == 2) {break@loop2 // 写成 break效果一样}println(i * j)}println("i = $i")
}

通过标签,我们可以直接中断外层循环,下面的例子中虽然有两层for循环,在执行到i == 0 && j == 2的时候,整个循环就退出了

loop1@for (i in 0..3) {loop2@for(j in 0 .. 4) {if (j == 2) {break@loop1}println(i * j)}println("i = $i")
}
// 此时只会输出两个0

Java中没有直接提供标签功能来中断外层循环,但可以通过一些其它方式来实现类似的功能:

boolean shouldBreak = false;
for (int i = 0; i < 4; i++) {for (int j = 0; j < 5; j++) {if (j == 2) {shouldBreak = true;break;}}if (shouldBreak) {break;}
}

虽然看起来挺牛,但我目前还没有需要用到这种场景的时候。一般情况下,我们可能会倾向于使用return,当需要跳出外层循环时,直接return,将逻辑封装在一个方法中。

fun breakTest() {val users = arrayOf(intArrayOf(1, 25),intArrayOf(2, 30),intArrayOf(3, 22),intArrayOf(4, 45),intArrayOf(5, 28))val ageLimit = 30var userFound = falseloop1@for (i in users.indices) {loop2@for (j in users[i].indices) {if (j == 1 && users[i][j] > ageLimit) { // j == 1 是年龄  println("找到用户 ID: " + users[i][0] + ",年龄: " + users[i][j])userFound = truebreak@loop1}}}if (!userFound) {println("未找到符合条件的用户。")}
}
public class UserSearch {  public static void main(String[] args) {  int[][] users = {  {1, 25},  {2, 30},  {3, 22},  {4, 45},  {5, 28}  };  int ageLimit = 30;  int userId = findFirstUserExceedingAge(users, ageLimit);  if (userId != -1) {  System.out.println("找到用户 ID: " + userId + ",年龄超过 " + ageLimit + " 岁。");  } else {  System.out.println("未找到符合条件的用户。");  }  }  public static int findFirstUserExceedingAge(int[][] users, int ageLimit) {  for (int[] user : users) {  if (user[1] > ageLimit) { // user[1] 是年龄  return user[0]; // 返回用户 ID  }  }  return -1; // 返回 -1 表示未找到  }  
}

Continue

continue与break基本一样,这里给一个简单的示例

loop1@for (i in 0..3) {loop2@for(j in 0 .. 4) {if (j == 2) {continue@loop1}println(i * j)}println("i = $i")
}
// 返回 0 0, 0 1, 0 2, 0 3

Return

Kotlin 中函数可以使用函数字面量、局部函数与对象表达式实现嵌套。 标签限定的 return 允许我们从外层函数返回。 最重要的一个用途就是从 lambda 表达式中返回。

fun foo() {listOf(1, 2, 3, 4, 5).forEach {if (it == 3) returnprint(it)}print("这一行不会被打印")// 输出 1 2
}fun foo1() {listOf(1, 2, 3, 4, 5).forEach lit@{if (it == 3) return@lit // 局部返回到该 lambda 表达式的调用者——forEach 循环print(it)}print("这一行会被打印")// 输出1 2 4 5 这一行会被打印
}fun foo2() {listOf(1, 2, 3, 4, 5).forEach{if (it == 3) return@forEachprint(it)}print("这一行会被打印")// 输出1 2 4 5 这一行会被打印
}

暂时还没有想到很好的实践,感觉Java原本的也够用了

异常

异常类

Kotlin 中所有异常类继承自 Throwable 类。 每个异常都有消息、堆栈回溯信息以及可选的原因。

使用 throw 表达式来抛出异常:

fun main() {throw Exception("Hi There!")
}

使用 try……catch 表达式来捕获异常:

try {// 一些代码
} catch (e: SomeException) {// 处理程序
} finally {// 可选的 finally 块
}

可以有零到多个 catch 块,finally 块可以省略。 但是 catchfinally 块至少需有一个。

try是一个表达式

try 是一个表达式,意味着它可以有一个返回值:

val a: Int? = try { input.toInt() } catch (e: NumberFormatException) { null }

try-表达式的返回值是 try 块中的最后一个表达式或者是(所有)catch 块中的最后一个表达式。 finally 块中的内容不会影响表达式的结果。

受检异常

什么是受检异常

在Java中,所有异常都继承自java.lang.Throwable

异常分为两种主要类型:受检异常Checked Exceptions和非受检异常Unchecked Exception

受检异常是指在编译时被检查的异常。Java 编译器会确保在处理受检异常的代码中,必须显式地处理这些异常,否则编译会失败。开发者可以通过以下两种方式来处理检查性异常:

  1. 捕获异常:使用 try-catch 块来捕获异常,进行适当的处理。

  2. 声明异常:在方法的签名中使用 throws 关键字声明该方法可能抛出的检查性异常。

常见的检查性异常示例

一些常见的检查性异常包括:

  • IOException:输入/输出操作失败或中断时抛出。

  • SQLException:数据库访问错误时抛出。

  • ClassNotFoundException:试图加载一个不存在的类时抛出。

受检异常的优势和劣势

优势:

  • 促使开发者显式处理异常,增强程序的健壮性。

  • 让调用者对可能的错误情况有清晰的了解。

劣势:

  • 可能导致代码过于复杂,特别是在多个方法嵌套的情况下,多层次的 try-catch 代码可能会影响可读性。

  • 过度使用受检异常可能会让方法签名变得冗长。

在Java中,我们常用的StringBuilder继承自AbstractStringBuilder,AbstractStringBuilder实现了接口Appendable

Appendable append(CharSequence csq) throws IOException;

如果正常实现的话,我们在使用StringBuilder时可能会这样:

try {log.append(message)
} catch (IOException e) {// 必须要安全
}

但实际上我们使用时都是直接append就可以了,这是因为我们操作StringBuilder都在内存中,不会出现IO异常,因此AbstractStringBuilder在实现时去掉了抛出IO异常:

@Override
public AbstractStringBuilder append(CharSequence s) {if (s == null) {return appendNull();}if (s instanceof String) {return this.append((String)s);}if (s instanceof AbstractStringBuilder) {return this.append((AbstractStringBuilder)s);}return this.append(s, 0, s.length());
}

Kotlin 没有受检异常

Bruce Eckel says this about checked exceptions:

通过一些小程序测试得出的结论是异常规范会同时提高开发者的生产力与代码质量,但是大型软件项目的经验表明一个不同的结论——生产力降低、代码质量很少或没有提高。

And here are some additional thoughts on the matter:

  • 《Java 的受检异常是一个错误》(Java's checked exceptions were a mistake)(Rod Waldhoff)

  • 《受检异常的烦恼》(The Trouble with Checked Exceptions)(Anders Hejlsberg)

If you want to alert callers about possible exceptions when calling Kotlin code from Java, Swift, or Objective-C, you can use the @Throws annotation. Read more about using this annotation for Java and for Swift and Objective-C.

Nothing类型

在 Kotlin 中 throw 是表达式,所以你可以使用它(比如)作为 Elviselvis.mp3 表达式的一部分:

val s = person.name ?: throw IllegalArgumentException("Name required")

throw 表达式的类型是 Nothing 类型。 这个类型没有值,而是用于标记永远不能达到的代码位置。 在你自己的代码中,你可以使用 Nothing 来标记一个永远不会返回的函数:

fun fail(message: String): Nothing {throw IllegalArgumentException(message)}

当你调用该函数时,编译器会知道在该调用后就不再继续执行了:

val s = person.name ?: fail("Name required")
println(s)     // 在此已知“s”已初始化

比较经常用到的一个场景是在编写Kotlin代码时遇到还未实现的部分,我们往往会给它标TODO,例如

private fun scanDeviceOrLocation(code: String) {lifecycleScope.launch {showLoading(true)val result = TODO()showLoading(false) // 这行代码不会被执行到}
}

这里我开了一个协程去做网络请求,但网络请求的方法还没有实现,所以先用TODO()代替。此时编译器会提示我下方的代码不会被执行到:

让我们看一下TODO方法的实现:

/*** Always throws [NotImplementedError] stating that operation is not implemented.*/@kotlin.internal.InlineOnly
public inline fun TODO(): Nothing = throw NotImplementedError()

直接抛了一个未实现的异常

当处理类型推断时还可能会遇到这个类型。这个类型的可空变体 Nothing? 有一个可能的值是 null。如果用 null 来初始化一个要推断类型的值,而又没有其他信息可用于确定更具体的类型时,编译器会推断出 Nothing? 类型:

val x = null           // “x”具有类型 Nothing?
val l = listOf(null)   // “l”具有类型 `List<Nothing?>

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

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

相关文章

第一次用Markdown

标题 标题2 标题3 标题4 字体 字体 字体姓名 性别 年龄张三 男 20![das]() baidu

【测试基础】web3.0介绍

web3.0介绍 Web3.0也被称为下一代互联网,是对当前互联网(Web2.0)的演进和升级。其目标是实现一个更加去中心化、安全、用户拥有数据主权且具有更好互操作性的互联网环境。Web3.0的核心技术包括区块链、智能合约和加密货币等。 web2.0与web3.0区别 Web2.0和Web3.0的主要区别在…

区块链原理、技术与实践

区块链介绍 区块链是一种分布式账本技术,允许多个参与者共同维护一个不断增长的数据记录列表,每个区块包含一系列交易记录,并通过密码学方法与前一个区块链接起来,形成一个不可篡改和不可逆的链条。 这种基于共识的机制使得区块链具有高度的安全性和透明性。 区块链与传统W…

《高效能人士的七个习惯》

情感账户 勇气和体谅 大石头 自传式回应、同理心倾听:用你的话反映他们的感受和意思,而不是去评论、去判断是否正确

高效能人士的七个习惯

情感账户 勇气和体谅 大石头 自传式回应、同理心倾听:用你的话反映他们的感受和意思,而不是去评论、去判断是否正确

新春“码”启 | Cocos 3D 微信小游戏(第5天):分包构建和上传发布(完美收官)

新春开发 Cocos 3D 微信小游戏计划的第 5 天,详细介绍了如何利用Cocos Creator开发并发布一款3D微信小游戏,包括游戏状态机的设计理念,和微信小游戏主包大小限制时的解决方案——分包策略。从游戏设计、开发、调试到最后成功发布的全过程,为想要进入微信小游戏开发领域的开…

爬虫随笔(一)

爬虫随笔,某牛前几天一直在看js逆向,现在分享一下本人近期学习记录首先分享一个网站,这个网站可以获得request所需要的header和cookie https://curlconverter.com/ 爬取网站就不挂了简单观察发现,该网站是滑动加载,我们可以在滑动加载时获得我们所需要的接口,发现两个链…

Flow-CLI 全新升级,轻松对接 Sonar 实现代码扫描和红线卡点

Flow-CLI 使用的典型场景如:自定义开发一个 Sonar 扫描步骤,以在流水中触发 Sonar 扫描,并以扫描结果作为红线卡点,以保证代码质量;对接三方自有审批平台,在发布前进行检查审批,审批通过才允许发布。场景介绍 Flow-CLI 是云效流水线 Flow 推出的一款命令行工具,帮助用户…

皮克定理

小蓝鸟的面积S=B/2+I-1

两步构建 AI 总结助手,实现智能文档摘要

本次解决方案将向您介绍,如何通过函数计算 FC 阿里云百炼平台搭建智能 AI 总结助手,实现高效的文本自动总结和信息提取。在信息极度丰富的当下,如何从海量且复杂的文件资料中筛选出关键内容,成为了不少企业和个人急需解决的问题。本次解决方案将向您介绍,如何通过函数计算…

自定义桌面——获取天气

最近天气时不时会降温,经常用手机查看天气,感觉好不方便,今天决定把天气的功能添加到“自定义桌面”来,为了免费获取天气数据,竟然花了差不多一天的时间来制作完成。(假如一天人工费几百来算,这样真的值得吗?非常值得!为未来完善功能添砖加瓦打好基础!) 添加天气的功…

2.6 第三张评估表搭建

今天完成了作业中第三张能力评估表的搭建 html界面(部分)如下:以下是前端代码(部分): <body> <h1 style="text-align: center">能力评估</h1> <div id="root" style="border: 1px solid black"><form id="…