Kotlin 初学者常见错误:从 A 到 Z

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

引言:为什么聪明的开发者也会在 Kotlin 上栽跟头



Kotlin 是一种优雅且强大的语言,赢得了数百万开发者的青睐。但它的简洁性和灵活性也暗藏陷阱,尤其是对于从 Java 或其他语言转来的初学者。许多人认为既然 Kotlin 比 Java “更容易理解”,那么错误就会更少。实际上恰恰相反:不理解语言哲学会导致难以排查的 bug。



在本文中,我们将分析 Kotlin 初学者常见错误——从忽视 null safety 到错误使用协程。每个错误都会配有代码示例和实用建议,帮助您避免它们。



1. Null Safety:何时“?”能救命,何时不能



1.1. 忽视可空类型

最常见的错误是在本可以使用安全调用的地方使用 !!(非空断言)。初学者经常这样写:



fun printLength(str: String?) {    println(str!!.length) // 如果 str == null,会抛出 NPE}


正确的方法是使用 ?.let 或 Elvis 操作符 ?::



fun printLength(str: String?) {    str?.let { println(it.length) } ?: println("字符串为 null")}


1.2. 忘记智能转换

Kotlin 会在 null 检查后自动进行类型转换,但初学者往往没有注意到这一点:



fun process(value: Any?) {    if (value is String) {        // value 已经是 String 类型,无需强制转换!        println(value.length) // 无需写成 (value as String).length    }}


建议:相信编译器。只有在 100% 确定值不为 null 且准备好因错误而崩溃时,才使用 !!。



2. Sealed Class 和 When:穷尽性检查的陷阱



2.1. 在 sealed class 中使用 else 分支

Sealed class 的设计目的是让编译器检查所有分支是否都已处理。但初学者经常添加 else,从而破坏了这一特性:



sealed class Result {    data class Success(val data: String) : Result()    data class Error(val message: String) : Result()}

fun handleResult(result: Result) { when (result) { is Result.Success -> println(result.data) is Result.Error -> println(result.message) else -> {} // 错误!如果添加新的子类,编译器不会警告 }}


正确做法是 不使用 else:



fun handleResult(result: Result) {    when (result) {        is Result.Success -> println(result.data)        is Result.Error -> println(result.message)    } // 如果未覆盖所有分支,编译器会报错}


2.2. 混淆 sealed class 和 enum

初学者经常在需要 sealed class 的地方选择 enum。请记住:enum 用于有限的常量集合,sealed class 用于每个子类包含不同数据的层次结构。



3. 作用域函数:apply、let、run、with、also——如何避免混淆



3.1. 混淆上下文:this vs it

最常见的混淆是初学者在需要使用 let(上下文为 it)的操作中使用了 apply(上下文为 this):



data class User(var name: String, var age: Int)

fun initUser(): User { return User("匿名", 0).apply { // this 指向 User name = "伊万" age = 25 }}

fun printUserInfo(user: User?) { user?.let { // it 指向 user println("姓名: ${it.name}, 年龄: ${it.age}") }}


3.2. 无意义的作用域函数链

初学者喜欢编写类似这样的链式调用:



list?.let { it.filter { it > 5 }.also { println(it) } }

博客

书籍推荐