当前位置: 首页 > article >正文

Kotlin 2.1.0 入门教程(十一)for、while、return、break、continue

for 循环

for 循环会遍历任何提供迭代器的对象。

for (item in collection) print(item)

for (int: Int in ints) {
    println(int)
}

for 循环会遍历任何提供迭代器的对象,这意味着该对象必须满足以下条件:

  • 具有一个成员函数或扩展函数 iterator(),该函数返回一个 Iterator<> 对象,而该对象:

    • 具有一个成员函数或扩展函数 next()

    • 具有一个成员函数或扩展函数 hasNext(),该函数返回一个 Boolean

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

要遍历一个数字范围,可以使用范围表达式。

for (i in 1..3) {
    print(i)
}

for (i in 6 downTo 0 step 2) {
    print(i)
}

对于范围或数组的 for 循环会被编译成基于索引的循环,而不会创建迭代器对象。

如果你想要通过索引遍历数组或列表,可以这样操作:

for (i in array.indices) {
    print(array[i])
}

或者,你可以使用 withIndex() 库函数。

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

while 循环

whiledo-while 循环会在条件满足的情况下持续执行其主体。

它们之间的区别在于条件检查的时机:

  • while 循环会先检查条件,如果条件满足,则执行主体,然后返回检查条件。

  • do-while 循环会先执行主体,然后才检查条件。如果条件满足,则循环重复执行。因此,do-while 的主体至少会执行一次,无论条件是否满足。

while (x > 0) {
    x--
}

do {
    val y = retrieveData()
} while (y != null) // y 在这里是可以访问的。

在循环中,Kotlin 支持传统的 breakcontinue 操作符。

返回和跳转

Kotlin 提供了三种结构化跳转表达式:

  • return:默认情况下,return 会从最近的封闭函数或匿名函数中返回。

  • break:会终止最近的封闭循环。

  • continue:会跳过当前循环的当前迭代,继续执行下一次迭代。

fun example() {
    for (i in 1..10) {
        if (i == 5) {
            return // 从函数 example 中返回。
        }
        println(i)
    }
}
fun example() {
    for (i in 1..10) {
        if (i == 5) {
            break // 终止最近的循环。
        }
        println(i)
    }
}
fun example() {
    for (i in 1..10) {
        if (i == 5) {
            continue // 跳过当前迭代,继续下一次迭代。
        }
        println(i)
    }
}

return 的作用范围:

  • 在匿名函数(如 fun 声明的函数)中,return 会从该函数返回。

  • Lambda 表达式中,return 会从最近的封闭函数返回,而不是从 Lambda 表达式返回。如果需要从 Lambda 表达式返回,可以使用标签。

breakcontinue 的作用范围:

  • breakcontinue 只能用于循环结构(如 forwhiledo-while)。

  • 如果需要从嵌套循环中跳出,可以使用标签。

如果需要从嵌套循环中跳出,或者从 Lambda 表达式中返回,可以使用标签。例如:

loop@ for (i in 1..3) {
    for (j in 1..3) {
        if (i == 2 && j == 2) {
            break@loop // 从带有标签的外层循环中跳出。
        }
        println("i = $i, j = $j")
    }
}

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

val s = person.name ?: return

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

Nothing 类型是 Kotlin 中的一个特殊类型,表示《无值》。它是一个不可实例化的类型,表示代码不会返回任何值,通常用于表示程序的退出点(如 returnthrow 等)。

在上面的例子中:

  • person.name 是一个可能为 null 的表达式。

  • ?:Kotlin 的空合并运算符(Elvis 运算符),用于提供一个默认值,当左侧表达式为 null 时,返回右侧的值。

  • 如果 person.name 不为 null,则 s 将被赋值为 person.name

  • 如果 person.namenull,则执行 return,表示从当前函数返回。

  • 由于 return 是一个跳转表达式,它不会返回任何值,因此它的类型是 Nothing。这意味着 val s = person.name ?: return 这行代码在 person.namenull 时,会直接从函数返回,而不会继续执行后续代码。

带标签的 breakcontinue

Kotlin 中,任何表达式都可以被标记上一个标签。

标签的格式是一个标识符后跟一个 @ 符号,例如 abc@fooBar@

要给一个表达式加上标签,只需在它前面添加一个标签即可。

fun main() {
    /*
    i = 1, j = 11
	i = 1, j = 12
	i = 1, j = 13
	i = 1, j = 14
	i = 1, j = 15
	i = 2, j = 11
	i = 2, j = 12
    */
    loop@ for (i in 1..5) {
        for (j in 11..15) {
            if (i == 2 && j == 13) {
                break@loop
            }
            println("i = $i, j = $j")
        }
    }
}
fun main() {
    /*
    i = 1, j = 11
    i = 1, j = 12
    i = 1, j = 13
    i = 1, j = 14
    i = 1, j = 15
    i = 2, j = 11
    i = 2, j = 12
    i = 3, j = 11
    i = 3, j = 12
    i = 3, j = 13
    i = 3, j = 14
    i = 3, j = 15
    i = 4, j = 11
    i = 4, j = 12
    i = 4, j = 13
    i = 4, j = 14
    i = 4, j = 15
    i = 5, j = 11
    i = 5, j = 12
    i = 5, j = 13
    i = 5, j = 14
    i = 5, j = 15
    */
    loop@ for (i in 1..5) {
        for (j in 11..15) {
            if (i == 2 && j == 13) {
                continue@loop
            }
            println("i = $i, j = $j")
        }
    }
}

在某些情况下,你可以在没有显式定义标签的情况下,非本地地(non-locally)使用 breakcontinue。这种非本地的用法在嵌套的内联函数中使用的 Lambda 表达式中是有效的。

breakcontinue 通常只能在它们所在的循环中使用。然而,在某些特定的上下文中,它们可以跨越多个作用域进行跳转,这种行为被称为《非本地跳转》。

当一个 Lambda 表达式被传递给一个内联函数(inline 函数)时,breakcontinue 可以在 Lambda 表达式中使用,而它们的作用范围会扩展到内联函数的调用点。这意味着你可以从 Lambda 表达式中跳转到外层的循环。

inline fun processList(list: List<Int>, action: (Int) -> Unit) {
    for (item in list) {
        action(item)
    }
}

fun main() {
    val numbers = listOf(1, 2, 3, 4, 5)
    processList(numbers) { number ->
        if (number == 3) {
            break // 非本地 break,跳出 processList 的循环。
        }
        println(number)
    }
}

这种非本地跳转允许你从 Lambda 表达式中直接跳出外层的循环。

只有在内联函数中使用的 Lambda 表达式才支持非本地跳转。这是因为内联函数会将 Lambda 表达式直接插入到调用点,从而允许跳转到外层的作用域。

带标签的 return

函数可以通过函数字面量、局部函数和对象表达式进行嵌套。带标签的 return 允许你从外层函数返回。

最重要的用例是从 Lambda 表达式中返回。要从 Lambda 表达式返回,需要给 Lambda 表达式加上标签,并使用带标签的 return

fun main() {
    fun foo() {
        listOf(1, 2, 3, 4, 5).forEach lit@ {
            if (it == 3) return@lit
            print(it)
        }
	}
    foo() // 1245
}

现在,它只从 Lambda 表达式返回。

通常,使用隐式标签会更加方便,因为这种标签的名称与 Lambda 表达式所传递到的函数名称相同。

fun main() {
    fun foo() {
        listOf(1, 2, 3, 4, 5).forEach {
            if (it == 3) return@forEach
            print(it)
        }
	}
    foo() // 1245
}

或者,你可以将 Lambda 表达式替换为匿名函数。在匿名函数中,return 语句将从匿名函数本身返回。

fun main() {
    fun foo() {
        listOf(1, 2, 3, 4, 5).forEach(fun (v: Int) {
            if (v == 3) return
            print(v)
        })
	}
    foo() // 1245
}

请注意,在前面的三个例子中,本地返回的使用与在普通循环中使用 continue 是类似的。

虽然没有直接等价于 break 的功能,但可以通过添加一个嵌套的 Lambda 表达式,并从其中非本地返回来模拟 break 的行为。

fun main() {
    // 12
    run loop@ {
        listOf(1, 2, 3, 4, 5).forEach {
            if (it == 3) return@loop
            print(it)
        }
	}
}
fun main() {
    // 12
    run loop@ {
        listOf(1, 2, 3, 4, 5).forEach(fun (v: Int) {
            if (v == 3) return@loop
            print(v)
        })
	}
}

当返回一个值时,解析器会优先考虑带标签的返回:

return@a 1

它是:在标签 @a 处返回值 1。而不是:返回一个带标签的表达式 @a 1

在某些情况下,你可以在不使用标签的情况下从 Lambda 表达式返回。这种非本地返回位于 Lambda 表达式中,但会退出封闭的内联函数。


http://www.kler.cn/a/539443.html

相关文章:

  • MongoDB 有哪些特性
  • Python 鼠标轨迹 - 防止游戏检测
  • 探秘AES加密算法:多种Transformation全解析
  • 【报错解决】MySQL报错:sql_mode=only_full_group_by
  • DeepSeek为何能爆火
  • leetcode 80. 删除有序数组中的重复项 II
  • 捕获一只比特币挖矿木马
  • vllm 部署 qwen2.5 报错2.5 报错404 已解决
  • java基础语法中阶
  • Docker Compose 容器卷映射:是否需要提前将文件拷贝到宿主机?
  • 【论文阅读笔记】HiDDeN:Hiding Data With Deep Networks
  • vue3中使用print-js组件实现打印操作
  • 蓝桥杯51单片机练习(国信长天比赛用)
  • c/c++蓝桥杯经典编程题100道(18)括号匹配
  • Win10+Ollama+AnythingLLM+DeepSeek构建本地多人访问知识库
  • 大数据示例:改变业务的 6 种方式
  • 【虚幻引擎UE】AOI算法介绍与实现案例
  • 【C++八股】std::atomic —— 原子操作
  • ASP.NET Core 如何使用 C# 向端点发出 POST 请求
  • openAI官方prompt技巧(二)
  • 基于springboot+vue的文物管理系统的设计与实现
  • android手机安装deepseek-r1:1.5b
  • DeepSeek开源多模态大模型Janus-Pro部署
  • 在 Linux 系统下,解压 `.tar.gz`
  • 14vue3实战-----获取用户信息和用户的菜单树信息
  • 解决Redisson在Kubernetes中连接旧Redis主节点的问题