【Kotlin】上手学习之控制流程篇

二、控制流程

2.1 条件与循环

2.1.1 if 表达式

在 Kotlin 中,if 是一个表达式:它会返回一个值。 因此就不需要三元运算符(条件 ? 然后 : 否则),因为普通的 if 就能胜任这个角色。

kotlin 复制代码
fun main() {
    val a = 2
    val b = 3

    var max = a
    if (a < b) max = b

    // With else
    if (a > b) {
      max = a
    } else {
      max = b
    }

    // 作为表达式
    max = if (a > b) a else b

    // You can also use `else if` in expressions:
    val maxLimit = 1
    val maxOrLimit = if (maxLimit > a) maxLimit else if (a > b) a else b

    // max is 3
    println("max is $max")
    // maxOrLimit is 3
    println("maxOrLimit is $maxOrLimit")
}

if 表达式的分支可以是代码块,这种情况最后的表达式作为该块的值:

kotlin 复制代码
val max = if (a > b) {
    print("Choose a")
    a
} else {
    print("Choose b")
    b
}

2.1.2 When 表达式

when 定义具有多个分支的条件表达式。它类似于类 Java 语言中的 switch 语句。它的简单形式如下所示。

kotlin 复制代码
when (x) {
    1 -> print("x == 1")
    2 -> print("x == 2")
    else -> {
        print("x is neither 1 nor 2")
    }
}

when将它的参数与所有的分支条件顺序比较,直到某个分支满足条件。

when既可以作为表达式使用也可以作为语句使用。如果它被当做表达式, 第一个符合条件的分支的值就是整个表达式的值,如果当做语句使用, 则忽略个别分支的值。 类似于if,每一个分支可以是一个代码块,它的值是块中最后的表达式的值。

如果其他分支都不满足条件将会求值else分支。 如果when作为一个表达式 使用,那么必须有else分支, 除非编译器能够检测出所有的可能情况都已经覆盖了, 例如,对于枚举(enum)类条目与密封(sealed)类子类型〕。

kotlin 复制代码
enum class Bit {
    ZERO, ONE
}

val numericValue = when (getRandomBit()) {
    Bit.ZERO -> 0
    Bit.ONE -> 1
    // 'else' is not required because all cases are covered
}

在when语句中,else分支在以下情况下是强制性的:

  • 当有Boolean、enum 或sealed类型或其可空对应类型的时候。
  • when 的分支并没有涵盖所有可能情况。
kotlin 复制代码
enum class Color {
    RED, GREEN, BLUE
}

when (getColor()) {  
    Color.RED -> println("red")
    Color.GREEN -> println("green")   
    Color.BLUE -> println("blue")
    // 'else' is not required because all cases are covered
}

when (getColor()) {
    Color.RED -> println("red") // no branches for GREEN and BLUE
    else -> println("not red") // 'else' is required
}

要定义多种情况的共同行为,请将它们的条件用逗号组合在一行中

kotlin 复制代码
when (x) {
    0, 1 -> print("x == 0 or x == 1")
    else -> print("otherwise")
}

可以用任意表达式(而不只是常量)作为分支条件

kotlin 复制代码
when (x) {
    s.toInt() -> print("s encodes x")
    else -> print("s does not encode x")
}

还可以检测一个值在(in)或者不在(!in)一个区间或者集合中:

kotlin 复制代码
when (x) {
    in1..10 -> print("x is in the range")
    in validNumbers -> print("x is valid")
    !in10..20 -> print("x is outside the range")
    else -> print("none of the above")
}

另一种选择是检测一个值是(is)或者不是(!is)一个特定类型的值。注意: 由于智能转换,你可以访问该类型的方法与属性而无需任何额外的检测。

kotlin 复制代码
fun hasPrefix(x: Any) = when(x) {
    is String -> x.startsWith("prefix")
    else -> false
}

when 也可以用来取代 if-else if 链。 如果不提供参数,所有的分支条件都是简单的布尔表达式,而当一个分支的条件为真时则执行该分支:

kotlin 复制代码
when {
    x.isOdd() -> print("x is odd")
    y.isEven() -> print("y is even")
    else -> print("x+y is odd")
}

可以使用以下语法将 when 的主语(subject,译注:指 when 所判断的表达式)捕获到变量中:

kotlin 复制代码
fun Request.getBody() =
    when (val response = executeRequest()) {
        is Success -> response.body
        is HttpError -> throw HttpException(response.status)
    }

when 主语中引入的变量的作用域仅限于when主体。

2.1.3 For 循环

for 循环可以对任何提供迭代器(iterator)的对象进行遍历,这相当于像 Java语言中的 foreach 循环。 for 的语法如下所示:

kotlin 复制代码
for (item in collection) print(item)

for循环体可以是一个代码块。

kotlin 复制代码
for (item: Int in ints) {
    // ......
}

如上所述,for 可以循环遍历任何提供了迭代器的对象。这意味着:

  • 有一个成员函数或者扩展函数 iterator() 返回 Iterator<>,其中:
    • 有一个成员函数或者扩展函数 next()
    • 有一个成员函数或者扩展函数 hasNext() 返回 Boolean

这三个函数都需要标记为operator

如需在数字区间上迭代,请使用区间表达式:

kotlin 复制代码
fun main() {
    for (i in 1..3) {
        // 1 
        // 2
        // 3
        println(i)
    }
    for (i in 6 downTo 0 step 2) {
        // 6
        // 4
        // 2
        // 0
        println(i)
    }
}

对区间或者数组的for循环会被编译为并不创建迭代器的基于索引的循环。

如果你想要通过索引遍历一个数组或者一个 list,你可以这么做:

kotlin 复制代码
fun main() {
    val array = arrayOf("a", "b", "c")
    for (i in array.indices) {
        println(array[i])
    }
}

或者你可以用库函数withIndex

kotlin 复制代码
fun main() {
    val array = arrayOf("a", "b", "c")
    for ((index, value) in array.withIndex()) {
        println("the element at $index is $value")
    }
}

2.1.4 while 循环

whiledo-while 当循环条件满足时会持续执行它们的主体。 它们之间的区别在于条件检查的时间:

  • while 先检查条件,如果满足,则执行主体,然后再返回到条件检查。
  • do-while 先执行主体,然后检查条件。如果满足,则循环重复。 所以 do-while 的主体至少执行一次,不管条件如何。
kotlin 复制代码
while (x > 0) {
    x--
}

do {
  val y = retrieveData()
} while (y != null) // y 在此处可见

2.2 返回与跳转

Kotlin 有三种结构化跳转表达式:

  • return 默认从最直接包围它的函数或者匿名函数返回。
  • break 终止最直接包围它的循环。
  • continue 继续下一次最直接包围它的循环。

所有这些表达式都可以用作更大表达式的一部分:

kotlin 复制代码
val s = person.name ?: return

这些表达式的类型是Nothing 类型

Break 与 Continue 标签

在 Kotlin 中任何表达式都可以用标签来标记。 标签的格式为标识符后跟 @ 符号,例如:abc@fooBar@。 要为一个表达式加标签,我们只要在其前加标签即可。

kotlin 复制代码
loop@ for (i in 1..100) {
    // ......
}

现在,我们可以用标签限定break或者continue

kotlin 复制代码
loop@ for (i in 1..100) {
    for (j in 1..100) {
        if (......) break@loop
    }
}

标签限定的 break 跳转到刚好位于该标签指定的循环后面的执行点。 continue 继续标签指定的循环的下一次迭代。

2.3 异常

2.3.1 异常类

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

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

kotlin 复制代码
fun main() {
    throw Exception("Hi There!")
}

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

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

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

2.3.2 Try 是一个表达式

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

kotlin 复制代码
val a: Int? = try { input.toInt() } catch (e: NumberFormatException) { null }

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

这里有点类似于

java 复制代码
public void readFile() {
    File source = new File("./test.txt");
    File target = new File("./test_target.txt");
    // Java7新特性,支持使用try后面跟随()括号管理释放资源
    try (InputStream fis = Files.newInputStream(source.toPath());
         OutputStream fos = Files.newOutputStream(target.toPath())) {
        byte[] buf = new byte[8192];
        int len;
        while ((len = fis.read(buf)) != -1) {
            fos.write(buf, 0, len);
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}

2.3.3 方法上抛出异常

直接在方法上方用@Throws抛出异常

kotlin 复制代码
@Throws(IOException::class)
fun writeUsingBufferedWriter(fileName: String, data: String) {
}

这个和Java有些不同

Java的是跟在方法后面用throws抛出异常

java 复制代码
public void writeUsingBufferedWriter(String fileName, String data) throws IOException 

而且 Kotlin 不强制开发者显式声明和处理异常,比如操作JSON数据的时候,Java会强制让显示的声明处理JSONException,但是Kotlin的不写也能正常编译过。

相关推荐
阿巴斯甜11 小时前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker12 小时前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq952713 小时前
Andorid Google 登录接入文档
android
黄林晴14 小时前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab1 天前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿1 天前
Android MediaPlayer 笔记
android
Jony_1 天前
Android 启动优化方案
android
阿巴斯甜1 天前
Android studio 报错:Cause: error=86, Bad CPU type in executable
android
张小潇1 天前
AOSP15 Input专题InputReader源码分析
android
_小马快跑_1 天前
Kotlin | 协程调度器选择:何时用CoroutineScope配置,何时用launch指定?
android