希望帮你在Kotlin进阶路上少走弯路,在技术上稳步提升。当然,由于个人知识储备有限,笔记中难免存在疏漏或表述不当的地方,也非常欢迎大家提出宝贵意见,一起交流进步。 ------ Android_小雨
整体目录:Kotlin 进阶不迷路:41 个核心知识点,构建完整知识体系
一、前言
1.1 Java Switch 的使用痛点(语法繁琐、穿透风险、类型局限)
用过Java的开发者对Switch语句肯定不陌生,它是处理多分支条件判断的常用工具,但在实际使用中,诸多痛点常常让人头疼不已。首先是语法繁琐 ,每个分支都需要写case关键字定义匹配条件,还得在分支末尾加break语句防止穿透,哪怕是简单的返回值逻辑,也得写一堆重复的结构。比如一个根据月份获取季节的逻辑,Java Switch写法就得嵌套多层case和break。
其次是穿透风险 ,这是Switch最容易踩的坑------一旦忘记写break,程序就会从匹配到的分支开始,依次执行后续所有分支的代码,导致逻辑错误。而且这种错误在编译时不会报错,只能在运行时发现,排查起来十分麻烦。
最后是类型局限,Java 7之前的Switch仅支持byte、short、int、char等基本类型,后续虽支持String和枚举,但仍无法匹配范围、类型判断等复杂场景。比如想判断一个数字是否在某个区间,或者判断一个对象的具体类型,Switch完全无能为力,只能转而使用繁琐的if-else嵌套。
java
// Java Switch 痛点示例:根据月份获取季节
public static String getSeason(int month) {
String season;
switch (month) {
case 3:
case 4:
case 5:
season = "春季";
break; // 忘记写就会穿透到夏季
case 6:
case 7:
case 8:
season = "夏季";
break;
case 9:
case 10:
case 11:
season = "秋季";
break;
case 12:
case 1:
case 2:
season = "冬季";
break;
default:
season = "月份无效";
}
return season;
}
1.2 Kotlin when 表达式的核心亮点(简洁、灵活、无穿透)
Kotlin的when表达式正是为解决Java Switch的痛点而生,它不仅是对Switch的替代,更是一次全面的升级,核心亮点体现在三个方面。第一是极致简洁 ,摒弃了case和break关键字,直接用"匹配条件 -> 执行逻辑"的结构,多值匹配用逗号分隔即可,代码量大幅减少。
第二是高度灵活,突破了Switch的类型局限,支持单值、多值、范围、类型、表达式等多种匹配方式,甚至可以无参数使用,实现自定义复杂条件判断。比如判断数字区间、检测对象类型、匹配表达式结果等场景,when表达式都能轻松应对。
第三是无穿透风险 ,when表达式的每个分支都是独立的,默认不会发生穿透,无需像Switch那样手动加break,从语法层面避免了穿透错误。同时,when还是表达式,能直接返回结果赋值给变量或作为函数返回值,进一步简化代码。
同样是"根据月份获取季节",用Kotlin when表达式改写后,逻辑清晰且简洁:
kotlin
// Kotlin when 表达式示例:根据月份获取季节
fun getSeason(month: Int): String {
return when (month) {
3, 4, 5 -> "春季"
6, 7, 8 -> "夏季"
9, 10, 11 -> "秋季"
12, 1, 2 -> "冬季"
else -> "月份无效"
}
}
1.3 本文核心内容预告(基础用法→进阶场景→实用技巧)
本文将从基础到进阶,全面解析Kotlin when表达式的使用方法。首先从入门级内容讲起,介绍when表达式的定义、基本语法以及与Java Switch的核心区别,帮助新手快速上手;接着深入讲解when表达式的多种灵活匹配场景,包括单值、多值、范围、类型和无参数匹配,覆盖实际开发中的常见需求;然后通过具体案例展示when表达式的实用场景,如替代多分支Switch、变量赋值、函数返回值等;最后总结when表达式的核心知识点、适用场景和避坑技巧,助力大家在开发中灵活运用,写出更简洁高效的Kotlin代码。
二、when 表达式 基础入门
2.1 什么是 when 表达式?(Kotlin 对 Switch 的优化升级)
when表达式是Kotlin中用于处理多分支条件判断的核心语法结构,是对Java Switch语句的彻底优化和升级。从本质上讲,when是一个表达式(而非语句),这意味着它会计算并返回一个结果,这个结果可以直接赋值给变量、作为函数返回值,或者嵌入到其他表达式中。
when表达式的核心思想是"匹配分支并执行对应逻辑",它会依次判断当前值与各个分支的匹配条件,找到第一个匹配的分支后执行相应逻辑,并返回该分支的结果(若为代码块则返回最后一行表达式的结果),之后不再执行后续分支(无穿透)。相比Switch,when不仅语法更简洁,匹配能力也更强大,是Kotlin流程控制中不可或缺的工具。
2.2 基本语法格式(简单条件匹配 + 直接赋值示例)
2.2.1 基本语法
when表达式的基本语法有两种形式:一种是带参数的形式(最常用),用于匹配指定参数的值;另一种是无参数形式,用于自定义复杂条件判断(后续进阶部分讲解)。带参数的基本语法如下:
kotlin
// 带参数的 when 表达式
when (参数) {
匹配条件1 -> 执行逻辑1 // 单值匹配
匹配条件2, 匹配条件3 -> 执行逻辑2 // 多值匹配
匹配条件4..匹配条件5 -> 执行逻辑3 // 范围匹配(后续讲解)
is 数据类型 -> 执行逻辑4 // 类型匹配(后续讲解)
else -> 默认执行逻辑 // 类似 Switch 的 default,匹配所有未覆盖的情况
}
说明:1. 匹配条件可以是常量、变量、表达式等;2. 执行逻辑若为单条语句,可直接写在箭头后,无需花括号;若为多条语句,需用花括号包裹,此时分支结果为最后一行语句的结果;3. else分支可选,但建议加上,避免遗漏未覆盖的情况(若编译器能确定所有情况都已覆盖,可省略)。
2.2.2 直接赋值示例
由于when是表达式,其结果可直接赋值给变量,这是Java Switch无法做到的。比如"根据星期几获取对应的中文名称":
kotlin
fun main() {
val day = 3 // 1-7 对应周一到周日
// when 表达式结果直接赋值给变量
val dayName = when (day) {
1 -> "星期一"
2 -> "星期二"
3 -> "星期三"
4 -> "星期四"
5 -> "星期五"
6 -> "星期六"
7 -> "星期日"
else -> "无效日期"
}
println("今天是:$dayName") // 输出:今天是:星期三
}
再比如执行逻辑为多条语句的场景,此时用花括号包裹,返回最后一行结果:
kotlin
fun getScoreDesc(score: Int): String {
return when (score) {
in 90..100 -> {
println("成绩优异")
"优秀" // 分支结果,作为整个 when 表达式的结果
}
in 80..89 -> {
println("成绩良好")
"良好"
}
else -> "其他"
}
}
fun main() {
println(getScoreDesc(95)) // 输出:成绩优异 优秀
}
2.3 与 Switch 的核心区别(无需 break、支持表达式返回值)
when表达式与Java Switch相比,有多个核心区别,正是这些区别让when更简洁、更灵活、更安全:
2.3.1 无需 break,无穿透风险
Java Switch的分支默认会穿透,必须在每个分支末尾加break才能终止执行,而when表达式的分支默认不穿透,找到第一个匹配的分支执行后就会终止,无需手动加break,从语法层面避免了穿透错误。
java
// Java Switch 穿透风险示例
public static void main() {
int num = 2;
switch (num) {
case 1:
System.out.println("一");
// 忘记加 break,会穿透到 case 2
case 2:
System.out.println("二");
case 3:
System.out.println("三");
}
// 输出:二 三(发生穿透)
}
kotlin
// Kotlin when 无穿透示例
fun main() {
val num = 2
when (num) {
1 -> println("一")
2 -> println("二")
3 -> println("三")
}
// 输出:二(无穿透)
}
2.3.2 支持表达式返回值,可直接赋值
Java Switch是语句,不返回值,无法直接赋值给变量,若要实现类似逻辑,需先定义变量再在分支中赋值;而when是表达式,会返回匹配分支的结果,可直接赋值给变量或作为函数返回值,代码更简洁。
java
// Java Switch 赋值需先定义变量
public static String getLevel(int score) {
String level;
switch (score / 10) {
case 10, 9 -> level = "A";
case 8 -> level = "B";
default -> level = "C";
}
return level;
}
kotlin
// Kotlin when 直接赋值
fun getLevel(score: Int): String {
return when (score / 10) {
10, 9 -> "A"
8 -> "B"
else -> "C"
}
// 更简洁的单表达式函数写法
// fun getLevel(score: Int) = when (score / 10) { 10,9 -> "A"; 8->"B"; else->"C" }
}
2.3.3 匹配能力更强大,支持多种匹配方式
Java Switch仅支持单值匹配(除了枚举和String),而when表达式支持单值、多值、范围、类型、表达式等多种匹配方式,能应对更复杂的场景(后续章节详细讲解)。
2.3.4 语法更简洁,无需 case 关键字
Java Switch需要用case定义每个分支的匹配条件,而when直接用"匹配条件 -> 执行逻辑"的结构,省去了大量冗余的case关键字,代码更简洁直观。
三、when 表达式 灵活匹配场景
when表达式的最大优势在于其灵活的匹配能力,除了类似Switch的单值匹配,还支持多种复杂的匹配场景,覆盖实际开发中的绝大多数需求。
3.1 单值匹配(直接匹配具体数值 / 字符串)
单值匹配是when最基础的用法,与Java Switch的单值匹配类似,用于匹配指定的常量值(如数值、字符串、枚举等)。匹配条件可以是字面量、常量变量或返回常量的表达式。
kotlin
// 字符串单值匹配
fun getFruitColor(fruit: String): String {
return when (fruit) {
"苹果" -> "红色"
"香蕉" -> "黄色"
"葡萄" -> "紫色"
else -> "未知颜色"
}
}
// 枚举单值匹配
enum class Direction { UP, DOWN, LEFT, RIGHT }
fun getDirectionDesc(direction: Direction): String {
return when (direction) {
Direction.UP -> "向上"
Direction.DOWN -> "向下"
Direction.LEFT -> "向左"
Direction.RIGHT -> "向右"
}
// 枚举匹配可省略枚举类名(需导入或在同一作用域)
// return when (direction) { UP -> "向上"; DOWN -> "向下"; ... }
}
fun main() {
println(getFruitColor("苹果")) // 输出:红色
println(getDirectionDesc(Direction.LEFT)) // 输出:向左
}
3.2 多值匹配(用逗号分隔多个匹配条件)
当多个匹配条件对应相同的执行逻辑时,可在一个分支中用逗号分隔多个匹配条件,实现"多值对应同一结果"的逻辑,无需像Java Switch那样写多个重复的case分支。
kotlin
// 多值匹配:判断是否为工作日
fun isWorkDay(day: Int): Boolean {
// 1-5 对应周一到周五,都返回 true
return when (day) {
1, 2, 3, 4, 5 -> true
6, 7 -> false
else -> throw IllegalArgumentException("无效的星期数")
}
}
// 多值匹配:根据考试等级获取奖励
fun getReward(level: String): String {
return when (level) {
"A", "A+" -> "500元奖学金"
"B", "B+" -> "200元奖学金"
"C" -> "学习用品"
else -> "鼓励信"
}
}
fun main() {
println(isWorkDay(3)) // 输出:true
println(getReward("A+")) // 输出:500元奖学金
}
3.3 范围匹配(in 关键字匹配区间)
when表达式支持通过in关键字匹配"值是否在指定范围内",范围可以是数值范围、字符范围等,这是Java Switch完全不具备的能力,能极大简化区间判断的逻辑。
kotlin
// 数值范围匹配:根据分数评级
fun getScoreLevel(score: Int): String {
require(score in 0..100) { "分数必须在 0-100 之间" }
return when (score) {
in 90..100 -> "优秀"
in 80..89 -> "良好"
in 60..79 -> "及格"
else -> "不及格" // 0-59 范围
}
}
// 字符范围匹配:判断字符类型
fun getCharType(c: Char): String {
return when (c) {
in 'a'..'z', in 'A'..'Z' -> "字母"
in '0'..'9' -> "数字"
else -> "特殊字符"
}
}
fun main() {
println(getScoreLevel(85)) // 输出:良好
println(getCharType('5')) // 输出:数字
println(getCharType('@')) // 输出:特殊字符
}
除了匹配"在范围内",还可以用!in匹配"不在范围内"的情况:
kotlin
// !in 匹配不在范围内的值
fun checkAge(age: Int): String {
return when (age) {
!in 0..120 -> "年龄无效"
in 0..17 -> "未成年"
else -> "成年"
}
}
3.4 类型匹配(is 关键字判断数据类型)
when表达式支持通过is关键字判断变量的具体数据类型,实现"类型匹配"逻辑,且匹配成功后会自动进行类型转换(智能类型转换),无需手动强转,这在处理多类型对象时非常实用。
kotlin
// 类型匹配:处理不同类型的数据
fun handleData(data: Any): String {
return when (data) {
is Int -> "整数类型,值为:$data" // 自动转换为 Int 类型
is String -> "字符串类型,长度为:${data.length}" // 自动转换为 String 类型
is Double -> "浮点数类型,值为:$data" // 自动转换为 Double 类型
is List<*> -> "列表类型,元素个数为:${data.size}" // 自动转换为 List 类型
else -> "未知类型"
}
}
fun main() {
println(handleData(100)) // 输出:整数类型,值为:100
println(handleData("Kotlin")) // 输出:字符串类型,长度为:6
println(handleData(listOf(1, 2, 3))) // 输出:列表类型,元素个数为:3
}
智能类型转换的优势在于,匹配成功后可直接调用该类型的方法和属性(如上述示例中的data.length、data.size),无需手动强转,既简洁又安全。
3.5 无参数匹配(自定义复杂条件判断)
when表达式还支持无参数的形式,此时每个分支的匹配条件是一个布尔表达式,当表达式结果为true时,执行对应的逻辑。这种方式适用于需要自定义复杂条件判断的场景,相当于多个独立的if-else分支,但语法更简洁。
kotlin
// 无参数 when:自定义复杂条件判断(判断学生是否符合奖学金条件)
fun isEligibleForScholarship(score: Int, attendance: Double, isExcellent: Boolean): Boolean {
return when {
// 条件1:成绩优秀且出勤率≥95%
score >= 90 && attendance >= 0.95 -> true
// 条件2:成绩良好且是优秀学生
score >= 80 && isExcellent -> true
// 条件3:其他情况不满足
else -> false
}
}
// 无参数 when:替代多层 if-else
fun getLoginStatus(username: String?, password: String?): String {
return when {
username.isNullOrEmpty() -> "用户名不能为空"
password.isNullOrEmpty() -> "密码不能为空"
username == "admin" && password == "123456" -> "登录成功"
else -> "用户名或密码错误"
}
}
fun main() {
println(isEligibleForScholarship(92, 0.96, false)) // 输出:true
println(getLoginStatus("admin", "")) // 输出:密码不能为空
}
无参数when的本质是"多个布尔条件的依次判断",找到第一个结果为true的条件分支执行,适用于各分支条件无共同参数的场景,比多层if-else更简洁易读。
四、when 表达式 实用场景举例
在实际开发中,when表达式的应用场景非常广泛,无论是简单的多分支判断,还是复杂的条件匹配,都能发挥其优势。下面介绍几个最常见的实用场景。
4.1 替代多分支 Switch 逻辑
这是when表达式最基础的应用场景,用于替代Java中繁琐的多分支Switch逻辑,尤其适合匹配枚举、字符串、数值等场景,代码更简洁且无穿透风险。
示例:根据支付方式处理支付逻辑(替代Java Switch)
java
// Java Switch 处理支付逻辑
public enum PaymentMethod { ALIPAY, WECHAT, CARD }
public static String processPayment(PaymentMethod method, double amount) {
switch (method) {
case ALIPAY:
return "支付宝支付 " + amount + " 元,已完成";
case WECHAT:
return "微信支付 " + amount + " 元,已完成";
case CARD:
return "银行卡支付 " + amount + " 元,已完成";
default:
throw new IllegalArgumentException("不支持的支付方式");
}
}
kotlin
// Kotlin when 替代 Switch 处理支付逻辑
enum class PaymentMethod { ALIPAY, WECHAT, CARD }
fun processPayment(method: PaymentMethod, amount: Double): String {
return when (method) {
PaymentMethod.ALIPAY -> "支付宝支付 $amount 元,已完成"
PaymentMethod.WECHAT -> "微信支付 $amount 元,已完成"
PaymentMethod.CARD -> "银行卡支付 $amount 元,已完成"
}
// 省略枚举类名的写法(需导入或同一作用域)
// return when (method) { ALIPAY -> "..."; WECHAT -> "..."; CARD -> "..." }
}
fun main() {
println(processPayment(PaymentMethod.ALIPAY, 100.0))
// 输出:支付宝支付 100.0 元,已完成
}
4.2 变量赋值(直接通过匹配结果赋值)
由于when是表达式,其结果可直接赋值给变量,这是非常常用的场景,能替代"定义变量+多分支赋值"的繁琐写法,让变量赋值逻辑更集中、更清晰。
示例:根据用户等级赋值对应的权限列表
kotlin
// 用户等级枚举
enum class UserLevel { ADMIN, MANAGER, USER, GUEST }
fun getPermissions(level: UserLevel): List<String> {
// when 表达式结果直接赋值给变量
val permissions = when (level) {
UserLevel.ADMIN -> listOf("增", "删", "改", "查", "管理")
UserLevel.MANAGER -> listOf("增", "删", "改", "查")
UserLevel.USER -> listOf("查", "改")
UserLevel.GUEST -> listOf("查")
}
return permissions
}
// 更简洁的单表达式函数写法
fun getPermissionsSimpler(level: UserLevel) = when (level) {
UserLevel.ADMIN -> listOf("增", "删", "改", "查", "管理")
UserLevel.MANAGER -> listOf("增", "删", "改", "查")
UserLevel.USER -> listOf("查", "改")
UserLevel.GUEST -> listOf("查")
}
fun main() {
println(getPermissions(UserLevel.MANAGER))
// 输出:[增, 删, 改, 查]
}
4.3 函数返回值(作为函数主体直接返回结果)
when表达式可直接作为函数的返回值,此时函数可简化为单表达式函数(省略花括号和return关键字),让函数定义更简洁,尤其适合逻辑简单的工具函数。
示例:1. 计算图形面积(根据图形类型和参数计算);2. 转换日期格式(将数字星期转换为中文)
kotlin
// 图形类型枚举
enum class Shape { CIRCLE, RECTANGLE, SQUARE }
// when 作为函数返回值(计算图形面积)
fun calculateArea(shape: Shape, vararg params: Double): Double {
return when (shape) {
Shape.CIRCLE -> {
val radius = params[0]
Math.PI * radius * radius // 圆的面积公式
}
Shape.RECTANGLE -> {
val width = params[0]
val height = params[1]
width * height // 矩形面积公式
}
Shape.SQUARE -> {
val side = params[0]
side * side // 正方形面积公式
}
}
}
// 单表达式函数:when 直接作为返回值
fun convertWeekToChinese(week: Int) = when (week) {
1 -> "星期一"
2 -> "星期二"
3 -> "星期三"
4 -> "星期四"
5 -> "星期五"
6 -> "星期六"
7 -> "星期日"
else -> "无效星期"
}
fun main() {
println(calculateArea(Shape.CIRCLE, 5.0)) // 输出:78.5398...
println(calculateArea(Shape.RECTANGLE, 3.0, 4.0)) // 输出:12.0
println(convertWeekToChinese(5)) // 输出:星期五
}
4.4 简化复杂条件判断(替代多层 if-else)
当存在多层if-else嵌套的复杂条件判断时,用无参数when表达式可将其转换为线性分支,消除嵌套缩进,提升代码可读性。这是when表达式比if-else更有优势的场景之一。
示例:用户注册信息校验(多层if-else嵌套 vs 无参数when)
kotlin
// 多层 if-else 嵌套:校验注册信息
fun checkRegisterInfoOld(username: String, password: String, phone: String): String {
if (username.isEmpty()) {
return "用户名不能为空"
} else {
if (username.length < 3) {
return "用户名长度不能小于3位"
} else {
if (password.isEmpty()) {
return "密码不能为空"
} else {
if (password.length < 6) {
return "密码长度不能小于6位"
} else {
if (!phone.matches("^1[3-9]\\d{9}$".toRegex())) {
return "手机号格式错误"
} else {
return "注册信息校验通过"
}
}
}
}
}
}
// 无参数 when 简化:校验注册信息(无嵌套)
fun checkRegisterInfoNew(username: String, password: String, phone: String): String {
return when {
username.isEmpty() -> "用户名不能为空"
username.length < 3 -> "用户名长度不能小于3位"
password.isEmpty() -> "密码不能为空"
password.length < 6 -> "密码长度不能小于6位"
!phone.matches("^1[3-9]\\d{9}$".toRegex()) -> "手机号格式错误"
else -> "注册信息校验通过"
}
}
fun main() {
println(checkRegisterInfoNew("kotlin", "123456", "13800138000"))
// 输出:注册信息校验通过
println(checkRegisterInfoNew("ko", "12345", "123456"))
// 输出:用户名长度不能小于3位
}
可以看到,无参数when将5层嵌套的if-else简化为线性分支,逻辑一目了然,后续维护和修改也更方便。
五、总结与使用建议
5.1 核心知识点回顾(when 表达式的灵活性与优势)
- 本质特性:when是表达式而非语句,会返回匹配分支的结果,可直接赋值给变量或作为函数返回值;默认无穿透,无需手动加break。
- 核心优势:相比Java Switch,语法更简洁(无case、break)、匹配能力更强(支持单值、多值、范围、类型、无参数匹配)、更安全(无穿透风险);相比if-else,更适合多分支场景,可消除嵌套缩进。
- 关键能力:支持智能类型转换(is匹配后自动转类型)、范围匹配(in关键字)、多值匹配(逗号分隔)、无参数自定义条件匹配,覆盖多种复杂场景。
5.2 适用场景与避坑点(何时优先用 when、避免过度复杂匹配)
5.2.1 适用场景
- 多分支条件判断场景:当存在3个及以上条件分支时,优先使用when,比if-else更简洁;替代Java的Switch语句(所有Switch场景都可用when替代)。
- 特定匹配场景:需要进行范围匹配(如数值区间)、类型匹配(如判断对象类型)、多值匹配(多个值对应同一逻辑)的场景,when是最优选择。
- 变量赋值或函数返回值场景:需要根据条件动态赋值变量,或函数逻辑为多分支判断并返回结果的场景,可利用when表达式的返回值特性简化代码。
- 复杂条件嵌套场景:存在多层if-else嵌套时,用无参数when可将嵌套拉平,提升可读性。
5.2.2 避坑点
- 避免遗漏else分支:除了编译器能确定所有情况都已覆盖的场景(如枚举匹配),建议加上else分支处理未覆盖的情况,避免运行时异常。
- 不要过度复杂单个分支:若某个分支的执行逻辑过于复杂(如包含大量循环、条件判断),应将其抽离为单独的函数,避免when表达式臃肿不堪,影响可读性。
- 区分带参数与无参数场景:当各分支有共同的匹配参数时,用带参数when;当各分支条件无共同参数(为独立布尔表达式)时,用无参数when,不要混用。
- 注意智能类型转换的范围:is匹配的智能类型转换仅在当前分支内有效,若需在分支外使用转换后的类型,需手动定义变量。
5.3 代码简洁性提升技巧(简化匹配逻辑、提高可读性)
- 利用单表达式函数简化 :当函数逻辑仅为when表达式时,用单表达式函数写法(省略花括号和return),代码更简洁。例如:
fun getLevel(score: Int) = when (score) { ... }。 - 合并重复逻辑的分支 :多个匹配条件对应相同逻辑时,用逗号分隔合并为一个分支,避免代码重复。例如:
1,2,3 -> "春季"。 - 优先处理简单或高频分支:将简单的分支或高频匹配的分支放在前面,复杂分支放在后面,提升代码执行效率和可读性。
- 用常量或枚举优化匹配条件 :避免在匹配条件中写字面量(如魔法数字、魔法字符串),改用常量或枚举,提升代码可维护性。例如:用
UserLevel.ADMIN替代"admin"。 - 无参数when的条件排序:无参数when的分支按"异常场景→普通场景→默认场景"排序,符合日常逻辑习惯,便于理解。
when表达式是Kotlin中最具代表性的语法特性之一,它的灵活性和简洁性能极大提升开发效率和代码质量。掌握when的各种匹配方式,并在合适的场景灵活运用,能让你的Kotlin代码更优雅、更易读、更易维护。建议在开发中主动用when替代Switch和复杂的if-else,逐步养成简洁的编码习惯。