快速掌握 Kotlin 核心语法,从变量到函数一网打尽
核心方法论:真相只有一个,语法规律就是线索
「一门编程语言的语法,就像一起案件的现场。每一个关键字都是一条线索,每一段代码都是一份证据。只要仔细观察、逻辑推理,就能还原出整套规律的真相。各位,跟我一起来破解 Kotlin 语法之谜吧!」
val vs var — 不可变与可变让我们从案件的第一条线索开始调查。Kotlin 的变量声明现场只留下了两个「嫌疑人」:val 和 var。先破解它们的名字密码 —— val 是 value(值)的缩写,意味着一旦赋值就不能再改变,强调的是「这是一个确定的值」;var 是 variable(变量)的缩写,意味着它的值可以随时变化。记住这两个英文原词,就永远不会搞混:value 不变,variable 可变。
下面这张对照表就是关键证据:
| 概念 | Kotlin | Java | C++ | Python |
|---|---|---|---|---|
| 不可变变量 | val |
final |
const / constexpr |
无直接等价物 |
| 可变变量 | var |
普通变量 | 普通变量 | 普通变量 |
现在,让我们来验证这条线索。看看实际的代码证据:
// val — 不可变(推荐优先使用)
val name = "Kotlin"
// name = "Java" // 编译错误!val 不能重新赋值
// var — 可变
var count = 0
count = 1 // OK,var 可以重新赋值
count = 2 // OK
柯南的推理笔记
根据大量代码现场的调查,我得出一个结论:默认使用 val,只在确实需要修改时才用 var。这能让代码更安全、更易于推理,也更适合并发编程。真相只有一个 —— 优先选择不可变!
val 近似于 C++ 中的 const 变量,var 则是普通变量。但要注意一个关键区别:Kotlin 中没有指针和引用的概念。C++ 里你需要区分 const int、const int*、int* const、const int& 等复杂组合,而 Kotlin 只需要 val 和 var 两个关键字就能搞定。此外,val 只意味着变量引用不可变 —— 如果指向一个可变对象(如 MutableList),对象本身的内容仍然可以修改,类似 C++ 中 T* const ptr(指针本身不可变,但指向的内容可变)。
接下来,我注意到一个重要线索:Kotlin 编译器有一项出色的「侦查能力」—— 类型推断。它可以根据赋值自动推断变量类型,不需要你显式声明。这就像我根据现场遗留的证据,就能推理出嫌疑人的身份一样!
// 编译器自动推断类型
val name = "Kotlin" // 推断为 String
val age = 25 // 推断为 Int
val pi = 3.14 // 推断为 Double
val isKotlinFun = true // 推断为 Boolean
从这段代码中,我们能推理出什么?对比 Java,Kotlin 简洁了很多:
// Java — 必须写类型
final String name = "Kotlin";
final int age = 25;
// Kotlin — 类型推断,省去冗余
val name = "Kotlin"
val age = 25
当然,有时候侦探也需要明确亮出证据。你可以手动指定类型,或者在没有初始值时必须指定 —— 这是 Kotlin 的铁律,不可违反:
// 显式声明类型
val age: Int = 25
val price: Double = 9.99
val greeting: String = "你好"
// 没有初始值时,必须显式声明类型
val result: Int
result = calculateSomething() // 稍后赋值(仅限一次)
const val在深入调查中,我发现了一个更严格的「嫌疑人」—— const val。它用于声明编译时常量,类似于 Java 中的 static final。它必须是顶层声明或 object 的成员,且值必须是基本类型或 String。这条线索表明,const val 的值在编译阶段就已经锁定,不可能在运行时改变:
// 编译时常量 — 在编译阶段就确定值
const val MAX_COUNT = 100
const val BASE_URL = "https://api.example.com"
const val PI = 3.14159265
// 对比 Java:
// public static final int MAX_COUNT = 100;
// public static final String BASE_URL = "https://api.example.com";
val vs const val
val 在运行时赋值(可以调用函数得到值),而 const val 在编译时就确定了值。const val 只能用于基本类型和 String。用排除法来记忆:能在编译期确定的用 const val,其余一律用 val。
接下来,我们来调查 Kotlin 数据类型的真相。这里有一条至关重要的线索:Kotlin 中没有 Java 的原始类型(primitive types),一切皆对象。但编译器会在可能的情况下优化为 JVM 原始类型 —— 这就好比嫌疑人表面上伪装成了对象,但在底层却悄悄地以原始类型的身份运行:
// 整数类型
val byte: Byte = 127 // 8 位
val short: Short = 32767 // 16 位
val int: Int = 2147483647 // 32 位
val long: Long = 9999999999L // 64 位,注意后缀 L
// 浮点数类型
val float: Float = 3.14f // 32 位,注意后缀 f
val double: Double = 3.14159265 // 64 位(默认)
// 数字字面量中可以用下划线增加可读性
val million = 1_000_000
val hexColor = 0xFF00FF
val binaryByte = 0b11010010
这是一条关键线索:Kotlin 中一切皆对象,没有 C++ 中 int / Integer(Java 的装箱类型)的区分,也没有原始类型(primitive types)。在 C++ 中你要操心 int、long、int32_t、size_t 等不同平台上的宽度差异,而 Kotlin 的 Int 始终是 32 位、Long 始终是 64 位,跨平台一致。此外,Kotlin 的 String 是一等公民对象,不像 C++ 中你需要在 char*、const char*、std::string、std::string_view 之间来回选择。Kotlin 中只有一种 String,内置丰富的方法,不需要引入任何头文件。
// Boolean
val isVisible: Boolean = true
val isEmpty = false
// Char — 单引号
val letter: Char = 'A'
val digit: Char = '9'
val chinese: Char = '中'
我在代码现场发现了一件「秘密武器」—— Kotlin 的字符串模板。经过推理,我断定这是 Kotlin 最实用的特性之一,它比 Java 的字符串拼接简洁太多了。让我们来收集证据:
val name = "Kotlin"
val version = 2.0
// 简单变量引用 — 用 $
val msg1 = "Hello, $name" // "Hello, Kotlin"
// 表达式 — 用 ${}
val msg2 = "$name $version 已发布" // "Kotlin 2.0 已发布"
val list = listOf("A", "B", "C")
val msg3 = "共 ${list.size} 个元素" // "共 3 个元素"
val msg4 = "大写: ${name.uppercase()}" // "大写: KOTLIN"
对比 Java 的字符串拼接 —— 证据确凿,Kotlin 的写法更胜一筹:
// Java — 冗长的字符串拼接
String msg = "Hello, " + name + "! 共 " + list.size() + " 个元素";
// Kotlin — 清晰的字符串模板
val msg = "Hello, $name! 共 ${list.size} 个元素"
Kotlin 还支持多行字符串(raw string),用三个双引号。这条线索在处理 JSON、SQL 等场景时尤其有用:
val json = """
{
"name": "$name",
"version": $version
}
""".trimIndent()
// 通用数组
val names: Array<String> = arrayOf("Alice", "Bob", "Charlie")
val numbers = arrayOf(1, 2, 3, 4, 5)
// 原始类型数组(性能更好,避免装箱)
val intArr: IntArray = intArrayOf(1, 2, 3)
val doubleArr: DoubleArray = doubleArrayOf(1.0, 2.0)
// 访问元素
println(names[0]) // "Alice"
names[1] = "Bobby" // 修改元素
注意了!这是一条容易让人「翻车」的线索。Kotlin 不会进行隐式类型转换,必须显式调用转换函数。在我看来,这种设计就像严格的法庭规则 —— 每一步操作都必须有明确的依据,不允许任何含糊不清的推理:
val intVal: Int = 42
// 错误!不能隐式转换
// val longVal: Long = intVal // 编译错误
// 正确 — 显式转换
val longVal: Long = intVal.toLong()
val doubleVal: Double = intVal.toDouble()
val stringVal: String = intVal.toString()
// 字符串转数字
val parsed: Int = "123".toInt()
val safeP: Int? = "abc".toIntOrNull() // 转换失败返回 null,不会崩溃
柯南的警告
Java 中 int 可以隐式转为 long,C++ 中更是充满了各种隐式转换陷阱(int 到 double、窄化转换等),但 Kotlin 中一律不行。忘记显式转换是从 Java 和 C++ 迁移过来时的常见「犯罪现场」。
if 是表达式案件进入了新的阶段 —— 控制流调查。我发现了第一条重要线索:在 Kotlin 中,if 不仅仅是语句,它还是一个表达式,可以有返回值。这意味着你不再需要三元运算符 ? :。用推理来理解:既然 if 能返回值,那三元运算符就成了多余的「嫌疑人」—— 被排除了!
// if 作为表达式 — 替代三元运算符
val a = 10
val b = 20
val max = if (a > b) a else b // max = 20
// 也可以带代码块
val description = if (a > b) {
println("a 更大")
"a wins" // 最后一个表达式是返回值
} else {
println("b 更大")
"b wins"
}
让我把各路「嫌疑人」摆在一起做个对比,这样真相就更清楚了:
// Java — 三元运算符
int max = (a > b) ? a : b;
// Python — 条件表达式
// max = a if a > b else b
// Kotlin — if 表达式(更可读)
val max = if (a > b) a else b
when 表达式现在,案件迎来了最精彩的转折 —— when 表达式登场!经过细致的排查,我可以确认它是 Kotlin 中替代 switch 的终极利器,功能强大得多。让我们逐步收集证据:
// 基本用法 — 匹配值
val dayOfWeek = 3
val dayName = when (dayOfWeek) {
1 -> "周一"
2 -> "周二"
3 -> "周三"
4 -> "周四"
5 -> "周五"
6, 7 -> "周末" // 多个值合并
else -> "未知" // 默认分支
}
从这段代码中,我们能推理出什么?when 还能匹配范围,这比 switch 灵活多了:
// 匹配范围
val score = 85
val grade = when (score) {
in 90..100 -> "优秀"
in 80..89 -> "良好"
in 70..79 -> "中等"
in 60..69 -> "及格"
else -> "不及格"
}
更令人惊讶的证据 —— when 还能匹配类型,并且自动进行智能转换:
// 匹配类型
fun describe(obj: Any): String = when (obj) {
is Int -> "整数: $obj"
is String -> "字符串,长度 ${obj.length}" // 智能类型转换!
is Boolean -> "布尔值: $obj"
else -> "未知类型"
}
还有最后一条线索 —— 无参 when,它可以作为 if-else 链的更优雅替代:
// 无参 when — 作为 if-else 链的替代(更优雅)
val temperature = 35
val weather = when {
temperature < 0 -> "严寒"
temperature < 10 -> "寒冷"
temperature < 25 -> "舒适"
temperature < 35 -> "炎热"
else -> "酷暑"
}
柯南的推理总结:when vs switch
经过全面调查,真相已经浮出水面:Kotlin 的 when 不需要 break,不会「穿透」到下一个分支。它也可以匹配范围、类型和任意条件,比 Java 的 switch 和 C++ 的 switch 强大得多。案件到此结束 —— when 就是王者!
for 循环与区间让我们继续追踪下一条线索 —— for 循环。Kotlin 的 for 循环与区间运算符搭配使用,形成了一套优雅的遍历体系:
// 遍历区间
for (i in 1..5) {
print("$i ") // 输出: 1 2 3 4 5
}
// until — 不包含末尾
for (i in 0 until 5) {
print("$i ") // 输出: 0 1 2 3 4
}
// downTo — 倒序
for (i in 5 downTo 1) {
print("$i ") // 输出: 5 4 3 2 1
}
// step — 指定步长
for (i in 0..10 step 2) {
print("$i ") // 输出: 0 2 4 6 8 10
}
遍历集合也留下了清晰的证据 —— Kotlin 提供了多种方式:
// 遍历集合
val fruits = listOf("苹果", "香蕉", "橙子")
for (fruit in fruits) {
println(fruit)
}
// 使用 indices 获取索引
for (i in fruits.indices) {
println("$i: ${fruits[i]}")
}
// 使用 withIndex() 同时获取索引和值
for ((index, fruit) in fruits.withIndex()) {
println("$index: $fruit")
}
// 输出:
// 0: 苹果
// 1: 香蕉
// 2: 橙子
while / do-while这两个嫌疑人的行为模式和其他语言几乎一致,不需要过多调查:
// while — 先判断后执行
var n = 5
while (n > 0) {
print("$n ") // 输出: 5 4 3 2 1
n--
}
// do-while — 先执行后判断(至少执行一次)
var input: String
do {
input = readLine() ?: ""
println("你输入了: $input")
} while (input != "quit")
将所有区间相关的线索汇总到一张证据表中:
| 表达式 | 含义 | 结果 |
|---|---|---|
1..10 |
闭区间(包含两端) | 1, 2, 3, ..., 10 |
1 until 10 |
半开区间(不含末尾) | 1, 2, 3, ..., 9 |
10 downTo 1 |
倒序 | 10, 9, 8, ..., 1 |
1..10 step 2 |
步长为 2 | 1, 3, 5, 7, 9 |
10 downTo 1 step 3 |
倒序 + 步长 | 10, 7, 4, 1 |
案件来到了「函数」这一关键现场。经过仔细调查,我发现 Kotlin 的函数定义规则十分清晰 —— 用 fun 关键字开头,参数类型在名称后面,返回类型紧随其后:
// 标准函数定义
fun add(a: Int, b: Int): Int {
return a + b
}
// 调用
val sum = add(3, 5) // 8
让我们把各路「嫌疑人」排列对比,真相自然浮现:
// Java
public int add(int a, int b) {
return a + b;
}
// Python
// def add(a, b):
// return a + b
// Kotlin
fun add(a: Int, b: Int): Int {
return a + b
}
Kotlin 函数有一个重要特征:它们总是属于某个类(成员函数),或者是顶层函数(直接写在文件中,不需要包在类里)。这和 C++ 不同 —— C++ 有自由函数(free functions)、类的成员函数、友元函数等多种形式。Kotlin 的顶层函数编译后实际上会变成一个包含静态方法的 Java 类,但在使用时你完全不需要关心这个细节。此外,Kotlin 的参数类型写在名称后面(a: Int),而 C++ 写在前面(int a),这一点要特别注意。
我在调查中发现了一条精妙的线索:当函数体只有一个表达式时,可以用 = 简写,返回类型也可以省略。这就是 Kotlin 追求简洁的决定性证据:
// 标准写法
fun add(a: Int, b: Int): Int {
return a + b
}
// 单表达式简写 — 省去花括号和 return
fun add(a: Int, b: Int) = a + b
// 更多示例
fun isEven(n: Int) = n % 2 == 0
fun greeting(name: String) = "Hello, $name!"
fun max(a: Int, b: Int) = if (a > b) a else b
又一条关键线索浮出水面 —— Kotlin 支持给函数参数设置默认值,从而大幅减少方法重载的需要。对一个侦探来说,这就像是找到了一个能替代多条分散线索的核心证据:
fun greet(name: String = "World", punctuation: String = "!") {
println("Hello, $name$punctuation")
}
greet() // Hello, World!
greet("Kotlin") // Hello, Kotlin!
greet("Kotlin", ".") // Hello, Kotlin.
对比 Java,不再需要写一堆重载方法了 —— 这是铁证:
// Java — 需要多个重载
void greet() { greet("World"); }
void greet(String name) { greet(name, "!"); }
void greet(String name, String punctuation) {
System.out.println("Hello, " + name + punctuation);
}
// Kotlin — 一个函数搞定
fun greet(name: String = "World", punctuation: String = "!") {
println("Hello, $name$punctuation")
}
C++ 也支持默认参数,写法是 void greet(string name = "World"),但有一些限制:默认参数必须从右往左连续提供,不能跳过中间的参数。而 Kotlin 配合命名参数,可以跳过任意中间参数,只指定你关心的那几个 —— 这比 C++ 灵活得多。此外,C++ 的默认参数值在声明处(头文件)绑定,而 Kotlin 的默认值编译后会生成合成方法来处理。
这条线索与默认参数联合使用时威力倍增。调用函数时可以指定参数名,让代码更清晰,也可以跳过中间的默认参数 —— 这是我在侦查中发现的最优雅的搭配组合:
fun createUser(
name: String,
age: Int = 0,
email: String = "",
isAdmin: Boolean = false
) {
println("$name, $age, $email, admin=$isAdmin")
}
// 使用命名参数 — 跳过中间参数,代码更易读
createUser(name = "Alice", isAdmin = true)
createUser(name = "Bob", email = "bob@example.com")
createUser(name = "Charlie", age = 30, email = "c@test.com")
varargfun printAll(vararg messages: String) {
for (msg in messages) {
println(msg)
}
}
printAll("Hello", "World", "Kotlin")
// 展开数组传入 vararg — 使用 * 展开运算符
val words = arrayOf("Hello", "World")
printAll(*words)
Unit 返回类型Unit 相当于 Java 的 void,表示函数不返回有意义的值。根据我的推理,省略不写是更常见的做法:
// 显式写 Unit
fun logMessage(msg: String): Unit {
println("[LOG] $msg")
}
// 省略 Unit(推荐)
fun logMessage(msg: String) {
println("[LOG] $msg")
}
柯南的案件总结:函数篇
经过对函数的全面调查,真相已经大白:Kotlin 的函数默认参数 + 命名参数的组合非常强大,可以替代 Java 中大量的方法重载和 Builder 模式。写代码时优先使用这些特性让 API 更简洁 —— 这就是我作为侦探给出的最终建议。
现在让我们把所有收集到的线索汇总到一张「案件证据总表」中。如果你有 Java、C++ 或 Python 经验,下面这张对照表能帮你快速建立映射关系 —— 就像把嫌疑人的指纹与数据库比对一样高效:
| 特性 | Kotlin | Java | C++ | Python |
|---|---|---|---|---|
| 不可变变量 | val x = 1 |
final int x = 1; |
const int x = 1; |
无内置支持 |
| 可变变量 | var x = 1 |
int x = 1; |
int x = 1; |
x = 1 |
| 类型推断 | val x = "hi" |
var x = "hi"; (Java 10+) |
auto x = "hi"s; (C++11) |
x = "hi" |
| Null 安全 | var x: String? = null |
@Nullable String x = null; |
std::optional<string> (C++17) |
x = None |
| 字符串模板 | "Hello, $name" |
"Hello, " + name |
std::format("Hello, {}", name) (C++20) |
f"Hello, {name}" |
| 类型转换 | x.toInt() |
(int) x 或 Integer.parseInt(x) |
static_cast<int>(x) |
int(x) |
| 函数定义 | fun add(a: Int, b: Int) = a + b |
int add(int a, int b) { return a + b; } |
int add(int a, int b) { return a + b; } |
def add(a, b): return a + b |
| 默认参数 | fun f(x: Int = 0) |
不支持(需重载) | void f(int x = 0) |
def f(x=0) |
| 命名参数 | f(name = "K") |
不支持 | 不支持(C++20 有指定初始化器) | f(name="K") |
| 条件表达式 | if (c) a else b |
c ? a : b |
c ? a : b |
a if c else b |
| 分支匹配 | when (x) { ... } |
switch (x) { ... } |
switch (x) { ... } |
match x: ... (3.10+) |
| 行尾分号 | 不需要 | 必须 | 必须 | 不需要 |
柯南的推理笔记:从 Java 迁移的关键心得
val 替代 final,用 var 替代普通声明new 关键字+when 替代 switch,更强大且不会穿透柯南的推理笔记:从 Python 迁移的关键心得
val/var 必须用关键字声明,不能直接赋值fun 而不是 def,参数必须声明类型{} 来界定代码块$ 而不是 f"" 和 {}从 C++ 迁移到 Kotlin,以下几点是最需要注意的「案件现场」:
*、&、->,所有变量都是引用语义.h/.cppnew/delete、unique_ptr/shared_ptrint 到 double 的隐式转换在 Kotlin 中必须显式完成#include、#define、#ifdef案件的调查告一段落,现在是时候检验你的推理能力了。以下练习将帮助你巩固本章学到的 Kotlin 基础语法线索。
声明以下变量,判断哪些应该用 val,哪些应该用 var:
对每个变量,让编译器推断类型(不要显式写类型),然后写出编译器会推断出的类型是什么。
想想每个变量在程序运行过程中是否需要被重新赋值。名字和圆周率是固定的,而年龄和计数器需要更新。推断类型时,记住:整数默认是 Int,带小数点的数默认是 Double,双引号括起来的文本是 String。
val name = "柯南" // String — 名字不变,用 val
var age = 17 // Int — 年龄会变,用 var
val pi = 3.14159265 // Double — 圆周率是常数,用 val
var counter = 0 // Int — 计数器需要累加,用 var
// 验证:可以修改 var,不能修改 val
age = 18 // OK
counter = counter + 1 // OK
// name = "新一" // 编译错误!val 不能重新赋值
编写一个函数 evaluateScore,接收一个 Int 类型的分数参数,使用 when 表达式返回对应的评价字符串:
注意 100 分需要单独匹配(用具体值),而其他区间用 in 关键字加区间运算符 .. 来匹配。考虑使用单表达式函数的写法。匹配顺序很重要 —— 把具体值匹配放在区间匹配前面。
fun evaluateScore(score: Int): String = when (score) {
100 -> "满分!完美推理!"
in 90..99 -> "优秀"
in 80..89 -> "良好"
in 60..79 -> "及格"
in 0..59 -> "不及格"
else -> "无效分数"
}
// 测试
println(evaluateScore(100)) // 满分!完美推理!
println(evaluateScore(85)) // 良好
println(evaluateScore(42)) // 不及格
println(evaluateScore(-1)) // 无效分数
给定以下变量:
val detective = "柯南"
val casesResolved = 312
val successRate = 99.7
用字符串模板写出一句话:「侦探柯南已成功破获 312 起案件,破案率高达 99.7%!」。要求:使用 $ 和 ${} 两种语法,至少一处使用表达式(如调用方法或做运算)。
简单的变量引用用 $variable,如果需要调用方法或写表达式则用 ${expression}。你可以尝试在模板中嵌入 ${casesResolved + 1} 之类的表达式来展示模板的灵活性,或者使用 ${detective.length} 之类的方法调用。
val detective = "柯南"
val casesResolved = 312
val successRate = 99.7
// 基础版
val report = "侦探$detective 已成功破获 $casesResolved 起案件,破案率高达 $successRate%!"
println(report)
// 侦探柯南已成功破获 312 起案件,破案率高达 99.7%!
// 进阶版 — 使用表达式
val report2 = "侦探${detective.uppercase()} 已成功破获 ${casesResolved} 起案件(名字共 ${detective.length} 个字),破案率 $successRate%!"
println(report2)
// 侦探柯南 已成功破获 312 起案件(名字共 2 个字),破案率 99.7%!
编写一个函数 formatClue,用于格式化案件线索。函数参数如下:
content: String — 线索内容(必填)caseNumber: Int — 案件编号,默认值为 1priority: String — 优先级,默认值为 "普通"isVerified: Boolean — 是否已验证,默认值为 false函数返回格式化后的字符串:"[案件#编号][优先级] 线索内容 (已验证/未验证)"。然后用命名参数调用这个函数 3 次:只传内容;传内容和优先级;传内容、案件编号和已验证标志。
利用默认参数避免重载。在函数体内用 if 表达式根据 isVerified 生成 "已验证" 或 "未验证" 的文本。调用时使用命名参数来跳过中间参数。
fun formatClue(
content: String,
caseNumber: Int = 1,
priority: String = "普通",
isVerified: Boolean = false
): String {
val status = if (isVerified) "已验证" else "未验证"
return "[案件#$caseNumber][$priority] $content ($status)"
}
// 调用 1:只传内容
println(formatClue("现场发现指纹"))
// [案件#1][普通] 现场发现指纹 (未验证)
// 调用 2:传内容和优先级(使用命名参数跳过 caseNumber)
println(formatClue(content = "发现可疑脚印", priority = "紧急"))
// [案件#1][紧急] 发现可疑脚印 (未验证)
// 调用 3:传内容、案件编号和已验证标志(跳过 priority)
println(formatClue(content = "嫌疑人不在场证明", caseNumber = 7, isVerified = true))
// [案件#7][普通] 嫌疑人不在场证明 (已验证)
编写一个函数 investigate,接收一个 Any 类型的参数(即任意类型),使用 when 表达式对传入的值进行「调查分析」,返回一段描述字符串:
Int 且为正数,返回 「正整数线索:值为 X」Int 且为 0 或负数,返回 「无效线索:整数 X 不是正数」String 且长度大于 0,返回 「文字线索:内容为 "X",共 N 个字符」String 且为空,返回 「空白线索:没有内容」Boolean 为 true,返回 「确认线索:已核实」Boolean 为 false,返回 「否定线索:已排除」::class.simpleName 获取类型名)这道题需要组合使用 when 的类型匹配(is)和无参 when 的条件判断。一种方式是先用 is 匹配类型,在匹配后利用智能转换进行额外判断。另一种方式是使用无参 when,在每个分支中同时检查类型和条件。记住,when 匹配到第一个满足的分支就会停止。
fun investigate(clue: Any): String = when {
clue is Int && clue > 0 -> "正整数线索:值为 $clue"
clue is Int -> "无效线索:整数 $clue 不是正数"
clue is String && clue.isNotEmpty() -> "文字线索:内容为 \"$clue\",共 ${clue.length} 个字符"
clue is String -> "空白线索:没有内容"
clue == true -> "确认线索:已核实"
clue == false -> "否定线索:已排除"
else -> "未知线索:类型为 ${clue::class.simpleName}"
}
// 测试
println(investigate(42)) // 正整数线索:值为 42
println(investigate(-5)) // 无效线索:整数 -5 不是正数
println(investigate("指纹")) // 文字线索:内容为 "指纹",共 2 个字符
println(investigate("")) // 空白线索:没有内容
println(investigate(true)) // 确认线索:已核实
println(investigate(false)) // 否定线索:已排除
println(investigate(3.14)) // 未知线索:类型为 Double