引言:为什么“整洁代码”与美学无关
当你刚开始用 Kotlin 编写代码时,似乎语言本身已经解决了一半的问题。没有 Java 那样的样板代码。有 `data class`、lambda 表达式、协程。但如果只是随意堆砌代码“想到哪写到哪”,一个月后连你自己都看不懂。如果项目是团队协作,同事会恨你(或者你恨他们)。
Kotlin 的整洁代码——不是关于用制表符还是空格。而是关于如何降低认知负荷,让逻辑透明,并避免隐藏在“方便”结构中的 bug。在本文中,我将展示具体技巧:从处理可空类型到使用作用域函数和密封类。全是干货,只有实践。
1. 空安全:如何避免滥用“?”和“!!”
Kotlin 以其类型系统为傲,但许多人写得就像 Java 加了“语法糖”一样。主要的反模式是 `?.let` 链和恐慌性的 `!!`。
糟糕(可读性差):
user?.address?.city?.let { city -> println(city)} ?: run { println("No city")}良好(优雅且整洁):
val city = user?.address?.cityprintln(city ?: "No city")只有在 100% 确定值不为 null 时才使用 `!!`(例如在测试中,或在 `if` 检查之后)。在生产环境中,这就像一颗手榴弹。如果你需要将可空类型转换为非空类型并处理两种情况,请使用 when 或 Elvis 运算符。
2. 作用域函数:`apply`、`run`、`with`、`let`、`also`——何时用哪个?
这是一个强大的工具,但经常被滥用。黄金法则是:不要嵌套作用域函数。如果你写了 `list.apply { ... }.let { ... }`,那就是在写意大利面条式代码。
选择规则:
apply—— 用于配置对象(返回对象本身)。非常适合构建器。let—— 用于对非空值执行操作(常与 `?.` 搭配使用)。run—— 当需要计算一个值并返回结果时使用。with—— 用于对同一对象进行分组调用而不返回结果。also—— 用于副作用(日志记录、验证)。
整洁示例:
data class User(var name: String, var age: Int)
fun createUser(): User = User("Unknown", 0).apply { name = "Alice" age = 30}不要写 user?.let { it.name } —— 这太冗余了。直接写 user?.name 即可。
3. 扩展函数:不要创建工具类
在 Java 中,你会写 StringUtils.capitalize(str)。在 Kotlin 中,则是 str.capitalize()。扩展函数是一种“扩写”其他类而不继承它的方式。
有用的扩展示例:
fun String.isValidEmail(): Boolean { return this.contains("@") && this.contains(".")}但不要过度使用。如果某个扩展函数只在一个地方使用,它可能应该是私有的。也不要对 `Any` 做扩展——这会破坏类型系统。
4. 密封类与 When:替代 if-else 的主要工具
当你有有限的状态集合时(例如 API 请求的结果),请使用 sealed class。这能确保你处理了所有情况。
正确做法:
sealed class NetworkResult<out T> { data class Success<T>(val data: T) : NetworkResult<T>() data class Error(val message: String, val code: Int) : NetworkResult<Nothing>() object Loading : NetworkResult<Nothing>()}
fun handle(result: NetworkResult<String>) { when (result) { is NetworkResult.Success -> println("Data: ${result.data}") is NetworkResult.Error -> println("Error: ${resu