1. 概述
本教程将介绍 Kotlin 语言中的 when{}
语法块,并展示其多种使用方式。
阅读本文需要你具备一定的 Kotlin 基础知识。如果你对 Kotlin 还不熟悉,可以先参考 Baeldung 的 Kotlin 入门指南。
2. Kotlin 的 when{}
语法块
✅ when{}
是 Java 中 switch-case
语句的增强版。
在 Kotlin 中,一旦某个分支匹配成功,只会执行该分支的代码块,执行完后会继续执行 when
块之后的语句。这意味着你 不再需要手动添加 break
语句 来防止“fall-through”。
为了演示 when{}
的用法,我们先定义一个枚举类,表示 Unix 文件类型权限字段中的第一个字符:
enum class UnixFileType {
HYPHEN_MINUS, // 普通文件
D, // 目录
L // 符号链接
}
2.1. 将 when{}
作为表达式使用
与 Java 的 switch
不同,Kotlin 中的 when{}
既可以作为 语句,也可以作为 表达式 使用。遵循函数式语言的理念,控制流结构本身就是表达式,其执行结果可以返回给调用方。
如果将 when{}
表达式的结果赋值给一个变量,编译器会检查返回值的类型是否与预期类型匹配,不匹配时会报错:
@Test
fun testWhenExpression() {
val directoryType = UnixFileType.D
val objectType = when (directoryType) {
UnixFileType.D -> "d"
UnixFileType.HYPHEN_MINUS -> "-"
UnixFileType.L -> "l"
}
assertEquals("d", objectType)
}
⚠️ 使用 when{}
作为表达式时需要注意两点:
- 返回值是匹配分支中最后一个表达式的值。
- 必须确保所有可能的输入值都被覆盖,否则编译器会报错。
2.2. 带默认分支的 when{}
表达式
默认分支使用 else
关键字表示,用于匹配未被其他分支覆盖的所有情况。
@Test
fun testWhenExpressionWithDefaultCase() {
val fileType = UnixFileType.L
val result = when (fileType) {
UnixFileType.L -> "linking to another file"
else -> "not a link"
}
assertEquals("linking to another file", result)
}
2.3. 抛出异常的 when{}
分支
✅ 在 Kotlin 中,throw
表达式返回的类型是 Nothing
,该类型是所有类型的子类型。
因此,即使 when{}
作为表达式使用,我们也可以在某个分支中抛出异常:
@Test(expected = IllegalArgumentException::class)
fun testWhenExpressionWithThrowException() {
val fileType = UnixFileType.L
val result: Boolean = when (fileType) {
UnixFileType.HYPHEN_MINUS -> true
else -> throw IllegalArgumentException("Wrong type of file")
}
}
2.4. 将 when{}
作为语句使用
当 when{}
作为语句使用时,不需要覆盖所有可能的值,每个分支返回的值也会被忽略:
@Test
fun testWhenStatement() {
val fileType = UnixFileType.HYPHEN_MINUS
when (fileType) {
UnixFileType.HYPHEN_MINUS -> println("Regular file type")
UnixFileType.D -> println("Directory file type")
}
}
2.5. 合并多个 when{}
分支条件
✅ Kotlin 允许通过逗号将多个条件合并到一个分支中,相当于逻辑“或”:
@Test
fun testCaseCombination() {
val fileType = UnixFileType.D
val frequentFileType: Boolean = when (fileType) {
UnixFileType.HYPHEN_MINUS, UnixFileType.D -> true
else -> false
}
assertTrue(frequentFileType)
}
2.6. 不带参数的 when{}
语法块
✅ 当省略 when{}
的参数时,它会退化为一个类似 if-else if
的结构,依次判断每个条件是否为 true
:
@Test
fun testWhenWithoutArgument() {
val fileType = UnixFileType.L
val objectType = when {
fileType === UnixFileType.L -> "l"
fileType === UnixFileType.HYPHEN_MINUS -> "-"
fileType === UnixFileType.D -> "d"
else -> "unknown file type"
}
assertEquals("l", objectType)
}
2.7. 动态条件表达式
✅ Kotlin 的 when{}
支持任意类型和动态表达式作为条件,不像 Java 的 switch
只能用于常量、枚举和字符串:
@Test
fun testDynamicCaseExpression() {
val unixFile = UnixFile.SymbolicLink(UnixFile.RegularFile("Content"))
when {
unixFile.getFileType() == UnixFileType.D -> println("It's a directory!")
unixFile.getFileType() == UnixFileType.HYPHEN_MINUS -> println("It's a regular file!")
unixFile.getFileType() == UnixFileType.L -> println("It's a soft link!")
}
}
2.8. 范围和集合匹配
✅ 使用 in
操作符可以判断参数是否在某个范围或集合中:
val number = 5
when (number) {
in 1..10 -> println("Between 1 and 10")
in listOf(15, 20, 25) -> println("It's 15, 20, or 25")
else -> println("Out of range")
}
2.9. is
操作符与智能类型转换
✅ 使用 is
操作符可以判断对象是否为某个类型,Kotlin 会自动进行智能类型转换(smart cast),无需显式转换:
@Test
fun testWhenWithIsOperatorWithSmartCase() {
val unixFile: UnixFile = UnixFile.RegularFile("Test Content")
val result = when (unixFile) {
is UnixFile.RegularFile -> unixFile.content
is UnixFile.Directory -> unixFile.children.map { it.getFileType() }.joinToString(", ")
is UnixFile.SymbolicLink -> unixFile.originalFile.getFileType()
}
assertEquals("Test Content", result)
}
2.10. 在循环中使用 when{}
✅ 从 Kotlin 1.4 开始,可以在 when{}
中使用 break
和 continue
控制循环:
val colors = setOf("Red", "Green", "Blue")
for (color in colors) {
when(color) {
"Red" -> break
"Green" -> continue
"Blue" -> println("This is blue")
}
}
在 Kotlin 1.4 之前,只能使用带标签的 break@LABEL
和 continue@LABEL
:
LOOP@ for (color in colors) {
when(color) {
"Red" -> break@LOOP
"Green" -> continue@LOOP
"Blue" -> println("This is blue")
}
}
3. 在 when{}
分支中执行多个语句
在每个分支中,我们可以通过大括号 {}
包裹多个语句,并且可以使用 return
等控制流语句:
fun isPositiveInt(number: Int): Boolean {
val result = when (number) {
0 -> {
println("number is zero.")
print("It's neither positive nor negative.")
return false
}
in -1 downTo Int.MIN_VALUE -> {
print("number is negative")
return false
}
else -> {
print("number is positive")
return true
}
}
return result
}
测试代码示例:
val outputStream = ByteArrayOutputStream()
System.setOut(PrintStream(outputStream))
val givenNumber = 0
val expectedOutput = "number is zero.\nIt's neither positive nor negative."
val isPositive = isPositiveInt(givenNumber)
assertFalse(isPositive)
assertEquals(expectedOutput, outputStream.toString())
4. 总结
虽然 Kotlin 的 when{}
不支持像 Scala 那样的模式匹配,但它足够灵活强大,完全可以替代传统 switch-case
的所有功能,甚至更胜一筹。
本文所有示例代码可在 GitHub 项目 中找到。