Kotlin 入门


语言哲学(Language Philosophy)

Kotlin 的设计不是凭空而来,而是基于三大核心哲学支柱。正如官方所说:"Kotlin is designed to be a pragmatic language that focuses on developer productivity."

简洁性原则(Conciseness Principle)

Kotlin 追求更少的样板代码(boilerplate code),让开发者专注于业务逻辑而非语法仪式。

Java
// Java: 定义一个简单的数据类需要 50+ 行
public class User {
    private String name;
    private int age;
    
    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public int getAge() { return age; }
    public void setAge(int age) { this.age = age; }
    
    @Override
    public boolean equals(Object o) { /* ... */ }
    @Override
    public int hashCode() { /* ... */ }
    @Override
    public String toString() { /* ... */ }
}
Kotlin
// Kotlin: 一行搞定,自动生成 equals/hashCode/toString/copy
data class User(val name: String, val age: Int)

安全性设计(Safety by Design)

Kotlin 将安全性内置于类型系统,而非依赖外部检查或开发者自觉。

"The type system is designed to eliminate the danger of null references from code." — Kotlin 官方文档

Kotlin
// 空安全(Null Safety)是类型系统的一部分
var name: String = "Kotlin"    // 非空类型,不能赋值 null
var nickname: String? = null   // 可空类型(Nullable Type),明确允许 null
 
// 编译器强制你处理 null 情况
val length = nickname?.length  // 安全调用(Safe Call),返回 Int?
val len = nickname?.length ?: 0  // Elvis 操作符,提供默认值

其他安全特性:

  • 智能类型转换(Smart Casts):类型检查后自动转换

  • 不可变引用(Immutable References)val 声明后不可重新赋值

  • 密封类(Sealed Classes):限制继承层次,when 表达式可穷尽检查

实用主义(Pragmatism)

Kotlin 不追求学术上的"纯粹",而是解决实际开发痛点

Kotlin
┌─────────────────────────────────────────────────────┐
│              Kotlin 的实用主义体现                   │
├─────────────────────────────────────────────────────┤
│  • 100% Java 互操作 → 渐进式迁移,不推倒重来          │
│  • 协程(Coroutines)→ 简化异步,不引入新线程模型      │
│  • 扩展函数 → 增强现有类,不修改源码                   │
│  • 多平台(Multiplatform)→ 共享逻辑,不强求统一 UI    │
└─────────────────────────────────────────────────────┘

实用主义的一个典型例子是扩展函数(Extension Function)

Kotlin
// 为 String 类添加方法,无需继承或修改源码
fun String.addExclamation() = this + "!"
 
val greeting = "Hello".addExclamation()  // "Hello!"

📝 练习题

题目1: 以下哪项最能体现 Kotlin 的"简洁性原则"?

A. 所有变量必须显式声明类型 B. data class 自动生成常用方法 C. 禁止使用 null 值 D. 必须使用分号结束语句

【答案】B

【解析】data class 自动生成 equals()hashCode()toString()copy() 等方法,大幅减少样板代码(boilerplate),是简洁性的典型体现。A 错误,Kotlin 支持类型推断;C 错误,Kotlin 允许可空类型;D 错误,Kotlin 分号是可选的。


题目2: Kotlin 的"实用主义"哲学意味着什么?

A. 追求类型系统的学术完美性

B. 优先解决实际开发痛点,支持渐进式采用

C. 完全抛弃 Java 生态,从零开始

D. 只支持函数式编程范式

【答案】B

【解析】Pragmatism 意味着 Kotlin 设计以解决实际问题为导向,比如与 Java 100% 互操作允许团队渐进式迁移(incremental adoption),而非强迫推倒重来。A 是学术主义;C 与事实相反;D 忽略了 Kotlin 的多范式特性。


Kotlin的历史与发展(History and Evolution)

JetBrains 的设计初衷(Design Motivation)

2010年,JetBrains 团队在开发 IntelliJ IDEA 时面临困境:

Java
┌─────────────────────────────────────────────────────────────┐
│                   JetBrains 当时的痛点                        │
├─────────────────────────────────────────────────────────────┤
1. 代码库庞大(millions of lines of Java code)              │
2. Java 语法冗长,开发效率受限                                │
3. 无法轻易切换到其他 JVM 语言(Scala 编译太慢)               │
4. 需要 100% 兼容现有 Java 代码和工具链                       │
└─────────────────────────────────────────────────────────────┘

              创造一门新语言:Kotlin(以俄罗斯岛屿命名)

JetBrains 的目标很明确:

"We wanted a language that would make us more productive while being fully compatible with our existing Java codebase."

Kotlin 的设计约束:

  • 编译速度:不能比 Java 慢太多(对比 Scala)

  • 互操作性:Java 调用 Kotlin、Kotlin 调用 Java 都要自然

  • 工具支持:IDE 支持必须是一等公民(first-class citizen)

  • 学习曲线:Java 开发者应该能快速上手

版本演进(Version Evolution)

Java
时间线 Timeline
─────────────────────────────────────────────────────────────────►
 
2011        2016         2017         2019        2021        2024
  │           │            │            │           │           │
  ▼           ▼            ▼            ▼           ▼           ▼
首次公开    1.0 正式版   Google 官宣   1.3 协程    1.5 稳定    2.0 K2编译器
Project    (Production   Android      正式版     Multiplatform  新纪元
Kotlin     Ready)        首选语言     Coroutines  进入 Beta

2017 年是转折点:Google 在 I/O 大会上宣布 Kotlin 成为 Android 官方开发语言(official language for Android development),这让 Kotlin 从 JetBrains 的内部项目一跃成为主流语言。


📝 练习题

题目: JetBrains 设计 Kotlin 的首要动机是什么?

A. 创造一门纯函数式编程语言

B. 替代 JavaScript 进行前端开发

C. 在保持与 Java 兼容的前提下提高开发效率

D. 开发专用于移动端的编程语言

【答案】C

【解析】JetBrains 拥有大量 Java 代码库,需要一门能与 Java 100% 互操作(100% interoperable)、同时更简洁高效的语言。这是"实用主义"的体现——解决自己的实际问题。Kotlin 后来被用于 Android/iOS/Web/Server 等多平台,但这是演进结果而非最初目标。


基本概念(Fundamental Concepts)

表达式优先(Expression-Oriented)

Kotlin 是一门 表达式优先(expression-oriented) 的语言。这意味着大多数控制结构都是表达式(expression),会返回一个值。

"In Kotlin, if is an expression: it returns a value." — Kotlin 官方文档

Kotlin
// Java 风格:if 是语句(Statement),不返回值
var max: Int
if (a > b) {
    max = a
} else {
    max = b
}
 
// Kotlin 风格:if 是表达式(Expression),返回值
val max = if (a > b) a else b  // 类似三元运算符,但更强大

语句与表达式的区别(Statement vs Expression)

Kotlin
┌─────────────────────────────────────────────────────────────┐
│                  Statement vs Expression                     │
├──────────────────────────┬──────────────────────────────────┤
│       语句 Statement      │         表达式 Expression         │
├──────────────────────────┼──────────────────────────────────┤
│  执行动作,不产生值         │  求值并返回结果                    │
│  不能作为赋值的右侧         │  可以赋值给变量                    │
│  Java 的 if/when/try     │  Kotlin 的 if/when/try
└──────────────────────────┴──────────────────────────────────┘

Kotlin 中的表达式示例:

Kotlin
// when 表达式(相当于增强版 switch)
val result = when (score) {
    in 90..100 -> "A"
    in 80..89 -> "B"
    in 60..79 -> "C"
    else -> "F"
}
 
// try 表达式
val number = try {
    input.toInt()
} catch (e: NumberFormatException) {
    0  // 解析失败时返回默认值
}
 
// 代码块表达式:最后一行是返回值
val greeting = {
    val hour = java.time.LocalTime.now().hour
    if (hour < 12) "Good morning" else "Good afternoon"
}()  // 立即执行

重要区别: 赋值语句(assignment)在 Kotlin 中不是表达式!

Kotlin
// Java:赋值返回被赋的值(可能导致 = 和 == 混淆的 bug)
// while ((line = reader.readLine()) != null) { ... }
 
// Kotlin:赋值是语句,不返回值
var a = 1
var b = 2
// val c = (a = b)  // ❌ 编译错误!赋值不是表达式

不可变性思想(Immutability Philosophy)

Kotlin 鼓励使用不可变数据(immutable data),这是函数式编程(functional programming)的核心原则之一。

"Prefer val over var." — Kotlin 编码规范

Kotlin
// val = value(不可变引用,Immutable Reference)
val name = "Kotlin"
// name = "Java"  // ❌ 编译错误:Val cannot be reassigned
 
// var = variable(可变引用,Mutable Reference)
var count = 0
count = 1  // ✅ 允许重新赋值

不可变 vs 只读的区别(Immutable vs Read-only):

Kotlin
val list = mutableListOf(1, 2, 3)  // list 引用不可变
list.add(4)  // ✅ 但内容可以修改!
 
// 真正的不可变集合
val immutableList = listOf(1, 2, 3)
// immutableList.add(4)  // ❌ 没有 add 方法
Kotlin
┌───────────────────────────────────────────────────────────────┐
val vs var 图解                            │
├───────────────────────────────────────────────────────────────┤
│                                                               │
val name ──────────► "Kotlin"
│        │                  ▲                                   │
│        │                  │ 引用不可变                          │
│        └──────────────────┘ (Reference is immutable)          │
│                                                               │
var count ─────────► 0
│        │                                                      │
│        └─────────────► 1   (Can be reassigned)                │
│                                                               │
val list ──────────► [1, 2, 3, 4]                           │
│        │                  ▲                                   │
│        │                  │ 引用不可变,但内容可变                │
│        └──────────────────┘ (Reference immutable,             │
│                              content mutable)                 │
└───────────────────────────────────────────────────────────────┘

为什么推崇不可变性?

  1. 线程安全(Thread Safety):不可变对象天然线程安全

  2. 可预测性(Predictability):数据不会被意外修改

  3. 易于调试(Easier Debugging):状态变化更容易追踪

  4. 函数式编程基础:纯函数(Pure Functions)不修改外部状态

Kotlin
// 不可变风格:创建新对象而非修改
data class User(val name: String, val age: Int)
 
val user1 = User("Alice", 25)
val user2 = user1.copy(age = 26)  // 创建新对象,原对象不变
 
println(user1)  // User(name=Alice, age=25)
println(user2)  // User(name=Alice, age=26)

📝 练习题

题目1: 以下关于 Kotlin 中 if 的说法,正确的是:

A. if 只能作为语句使用,不能返回值 B. if 是表达式,可以返回值并赋给变量 C. if 表达式必须有 else 分支 D. if 作为表达式时必须用花括号包裹

【答案】B

【解析】Kotlin 中 if 是表达式(expression),会返回一个值。例如 val max = if (a > b) a else b。但注意:当 if 作为表达式(需要返回值)时,必须else 分支,否则编译器不知道另一种情况返回什么。选项 C 的表述不完整——只有用作表达式时才必须有 else。选项 D 错误,单表达式不需要花括号。


题目2: 以下代码的输出是什么?

Kotlin
val list = mutableListOf("A", "B")
list.add("C")
println(list.size)

A. 编译错误,因为 val 不可变 B. 运行时错误 C. 2 D. 3

【答案】D

【解析】val 表示引用不可变(immutable reference),即 list 变量不能指向另一个列表。但 list 指向的是 MutableList,其内容是可变的。这就是"只读引用 vs 不可变对象"的区别。如果想要真正的不可变列表,应使用 listOf() 返回的 List<T>(没有 add 方法)。


与Java的关系(Relationship with Java)

Kotlin 从诞生之日起就被设计为与 Java 无缝协作。这不是事后补丁,而是核心设计目标。

"Kotlin is designed with Java interoperability in mind. Existing Java code can be called from Kotlin in a natural way, and Kotlin code can be used from Java rather smoothly as well." — Kotlin 官方文档

100%互操作性(100% Interoperability)

Kotlin
┌─────────────────────────────────────────────────────────────────┐
│                    Kotlin ↔ Java 互操作                         │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│    ┌──────────────┐                    ┌──────────────┐         │
│    │    Kotlin    │ ──── 调用 ────►    │     Java     │         │
│    │     Code     │ ◄─── 调用 ────     │     Code     │         │
│    └──────────────┘                    └──────────────┘         │
│           │                                   │                 │
│           └───────────┬───────────────────────┘                 │
│                       ▼                                         │
│              ┌─────────────────┐                                │
│              │   JVM Bytecode  │                                │
│              │   (.class 文件)  │                                │
│              └─────────────────┘                                │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

Kotlin 调用 Java:直接调用,无需任何包装

Kotlin
// Kotlin 代码直接使用 Java 类
import java.util.ArrayList
import java.time.LocalDateTime
 
fun main() {
    // 调用 Java 集合
    val list = ArrayList<String>()
    list.add("Kotlin")
    list.add("Java")
    
    // 调用 Java 时间 API
    val now = LocalDateTime.now()
    println("Current time: $now")
    
    // 调用 Java 静态方法
    val maxValue = Integer.MAX_VALUE
}

Java 调用 Kotlin:同样自然

Kotlin
// User.kt
class User(val name: String, val age: Int) {
    fun greet() = "Hello, I'm $name"
    
    companion object {
        @JvmStatic  // 让 Java 可以用 User.create() 调用
        fun create(name: String) = User(name, 0)
    }
}
Java
// Main.java
public class Main {
    public static void main(String[] args) {
        // Java 调用 Kotlin 类
        User user = new User("Alice", 25);
        System.out.println(user.getName());  // 自动生成 getter
        System.out.println(user.greet());
        
        // 调用伴生对象的静态方法
        User newUser = User.create("Bob");
    }
}
Kotlin
// 默认参数 + @JvmOverloads = Java 可用的多个重载
class HttpClient {
    @JvmOverloads
    fun request(
        url: String,
        method: String = "GET",
        timeout: Int = 5000
    ) { /* ... */ }
}
 
// Java 可以这样调用:
// client.request("http://...")
// client.request("http://...", "POST")
// client.request("http://...", "POST", 10000)

迁移路径(Migration Path)

从 Java 迁移到 Kotlin 是一个渐进式过程(incremental process),不需要一次性重写。

Kotlin
┌─────────────────────────────────────────────────────────────────┐
│                  Java → Kotlin 迁移策略                          │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  Phase 1: 新代码用 Kotlin
│  ─────────────────────────                                      │
│  • 新功能、新模块用 Kotlin 编写                                    │
│  • 现有 Java 代码保持不变                                         │
│                                                                 │
│  Phase 2: 逐步转换(Gradual Conversion)                         │
│  ─────────────────────────────────────────                      │
│  • IntelliJ 的 "Convert Java File to Kotlin" 功能               │
│  • 优先转换:工具类、数据类、独立模块                               │
│  • 转换后 review + 优化(利用 Kotlin 特性重构)                    │
│                                                                 │
│  Phase 3: 深度 Kotlin
│  ───────────────────────                                        │
│  • 使用协程替换回调/RxJava                                        │
│  • 使用扩展函数优化 API                                           │
│  • 使用密封类替换枚举 + 状态模式                                   │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

自动转换示例:

Java
// Java 原始代码
public class StringUtils {
    public static boolean isNullOrEmpty(String s) {
        return s == null || s.isEmpty();
    }
    
    public static String capitalize(String s) {
        if (isNullOrEmpty(s)) return s;
        return Character.toUpperCase(s.charAt(0)) + s.substring(1);
    }
}
Kotlin
// IntelliJ 自动转换结果(可进一步优化)
object StringUtils {
    fun isNullOrEmpty(s: String?): Boolean {
        return s == null || s.isEmpty()
    }
    
    fun capitalize(s: String?): String? {
        if (isNullOrEmpty(s)) return s
        return s!![0].uppercaseChar().toString() + s.substring(1)
    }
}
 
// 进一步 Kotlin 化优化
fun String?.isNullOrEmpty(): Boolean = this == null || this.isEmpty()
 
fun String?.capitalize(): String? = 
    this?.takeIf { it.isNotEmpty() }
        ?.let { it[0].uppercaseChar() + it.substring(1) }

共存策略(Coexistence Strategy)

在实际项目中,Java 和 Kotlin 长期共存是常态。

Kotlin
┌─────────────────────────────────────────────────────────────────┐
│                     混合项目结构示例                              │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  project/
│  ├── src/main/
│  │   ├── java/           ← 现有 Java 代码                        │
│  │   │   ├── com/app/legacy/
│  │   │   └── com/app/utils/
│  │   └── kotlin/         ← 新 Kotlin 代码                        │
│  │       ├── com/app/features/
│  │       └── com/app/extensions/
│  └── build.gradle.kts    ← Kotlin DSL 构建脚本                   │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

共存最佳实践:

Kotlin
// 1. 用扩展函数增强 Java 类,而非继承
fun Date.toLocalDate(): LocalDate = 
    this.toInstant().atZone(ZoneId.systemDefault()).toLocalDate()
 
// 2. 用 Kotlin 包装 Java API,提供更友好的接口
class KotlinHttpClient(private val javaClient: OkHttpClient) {
    suspend fun get(url: String): Response = withContext(Dispatchers.IO) {
        val request = Request.Builder().url(url).build()
        javaClient.newCall(request).execute()
    }
}
 
// 3. 在边界处处理平台类型(Platform Types)
fun processJavaList(list: MutableList<String>?) {  // 明确可空性
    list?.forEach { println(it) }
}

📝 练习题

题目1: 以下哪个注解可以让 Kotlin 的伴生对象方法在 Java 中像静态方法一样调用?

A. @JvmField B. @JvmStatic C. @JvmOverloads D. @JvmName

【答案】B

【解析】@JvmStatic 用于伴生对象(companion object)或命名对象(named object)中的方法,使其在字节码中生成真正的静态方法。这样 Java 代码可以用 ClassName.methodName() 直接调用,而不是 ClassName.Companion.methodName()@JvmField 用于属性;@JvmOverloads 用于生成默认参数的重载;@JvmName 用于自定义名称。


题目2: 关于 Java 与 Kotlin 共存,以下说法错误的是:

A. 同一个项目中可以同时包含 .java 和 .kt 文件

B. Kotlin 代码可以直接调用 Java 类和方法

C. 将 Java 文件转换为 Kotlin 后必须立即删除原 Java 文件

D. 迁移过程可以是渐进式的,新功能优先用 Kotlin

【答案】C

【解析】Java 到 Kotlin 的迁移是渐进式的(incremental),不存在"必须立即删除"的要求。IntelliJ 的转换功能会自动处理文件替换。实际项目中,团队可以根据自己的节奏逐步迁移,Java 和 Kotlin 代码可以长期共存并相互调用。


多范式编程(Multi-Paradigm Programming)

Kotlin 是一门多范式语言(multi-paradigm language),融合了面向对象(OOP)、函数式(FP)和命令式(Imperative)编程的优点。

"Kotlin combines object-oriented and functional programming features, allowing you to use the best tool for each task."

Kotlin
┌─────────────────────────────────────────────────────────────────┐
│                  Kotlin 的多范式融合                             │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│         ┌─────────────────┐                                     │
│         │   面向对象 OOP   │                                     │
│         │  类、继承、接口   │                                     │
│         └────────┬────────┘                                     │
│                  │                                              │
│    ┌─────────────┼─────────────┐                                │
│    ▼             ▼             ▼                                │
│  ┌─────┐    ┌─────────┐    ┌─────┐                              │
│  │命令式│    │ Kotlin  │    │函数式│                              │
│  │     │───►│  代码   │◄───│ FP  │                              │
│  │循环  │    │         │    │高阶  │                              │
│  │状态  │    │         │    │函数  │                              │
│  └─────┘    └─────────┘    └─────┘                              │
│                                                                 │
"Use the right paradigm for the right problem"
└─────────────────────────────────────────────────────────────────┘

面向对象(Object-Oriented Programming)

Kotlin 完整支持 OOP,但比 Java 更简洁、更安全。

Kotlin
// 类与继承(Classes and Inheritance)
open class Animal(val name: String) {  // open 才能被继承
    open fun speak() = "..."
}
 
class Dog(name: String, val breed: String) : Animal(name) {
    override fun speak() = "Woof!"  // 必须显式 override
}
 
// 接口与多实现(Interfaces)
interface Runnable {
    fun run()
    val speed: Int  // 接口可以有抽象属性
}
 
interface Swimmer {
    fun swim() = println("Swimming...")  // 接口可以有默认实现
}
 
class Duck : Animal("Duck"), Runnable, Swimmer {
    override fun run() = println("Running at $speed")
    override val speed = 5
    override fun speak() = "Quack!"
}
 
// 数据类(Data Classes):专为"数据容器"设计
data class Point(val x: Int, val y: Int)
 
// 密封类(Sealed Classes):限制继承层次
sealed class Result<out T> {
    data class Success<T>(val data: T) : Result<T>()
    data class Error(val message: String) : Result<Nothing>()
    object Loading : Result<Nothing>()
}

函数式编程(Functional Programming)

Kotlin 提供一流的函数式编程支持:高阶函数(Higher-Order Functions)Lambda 表达式不可变数据

Kotlin
// 函数是一等公民(First-Class Citizens)
val double: (Int) -> Int = { it * 2 }
val numbers = listOf(1, 2, 3, 4, 5)
 
// 高阶函数:函数作为参数或返回值
val doubled = numbers.map(double)       // [2, 4, 6, 8, 10]
val evens = numbers.filter { it % 2 == 0 }  // [2, 4]
val sum = numbers.reduce { acc, n -> acc + n }  // 15
 
// 链式调用(Method Chaining)
val result = numbers
    .filter { it > 2 }
    .map { it * it }
    .sortedDescending()
    .take(2)
// [25, 16]

常用高阶函数速查:

Kotlin
┌───────────────────────────────────────────────────────────────┐
│                  集合操作高阶函数                               │
├────────────┬──────────────────────────────────────────────────┤
│   函数      │                    作用                          │
├────────────┼──────────────────────────────────────────────────┤
│ map        │ 转换每个元素 Transform each element               │
│ filter     │ 过滤元素 Keep elements matching predicate         │
│ reduce     │ 累积为单值 Reduce to single value
│ fold       │ 带初始值的累积 Reduce with initial value
│ flatMap    │ 映射并展平 Map and flatten                        │
│ groupBy    │ 分组 Group elements by key                        │
│ partition  │ 分成两组 Split into two lists                     │
│ any/all    │ 存在/全部满足 Check predicate                     │
│ find       │ 查找第一个 Find first matching                    │
│ associate  │ 转为 Map Convert to Map                           │
└────────────┴──────────────────────────────────────────────────┘
Kotlin
// 实际示例:处理用户数据
data class User(val name: String, val age: Int, val city: String)
 
val users = listOf(
    User("Alice", 28, "Beijing"),
    User("Bob", 35, "Shanghai"),
    User("Charlie", 28, "Beijing")
)
 
// 按城市分组,统计每个城市的平均年龄
val avgAgeByCity = users
    .groupBy { it.city }
    .mapValues { (_, users) -> users.map { it.age }.average() }
// {Beijing=28.0, Shanghai=35.0}

命令式编程(Imperative Programming)

Kotlin 保留命令式风格,适用于需要明确控制流程和状态的场景。

Kotlin
// 传统循环仍然支持
fun findFirstNegative(numbers: List<Int>): Int? {
    for (num in numbers) {
        if (num < 0) {
            return num
        }
    }
    return null
}
 
// 可变状态在某些场景下更高效
fun buildString(items: List<String>): String {
    val sb = StringBuilder()  // 可变对象
    for (item in items) {
        sb.append(item).append(", ")
    }
    return sb.removeSuffix(", ").toString()
}

范式融合的艺术(The Art of Fusion)

真正的 Kotlin 高手懂得在合适的场景选择合适的范式

Kotlin
// 混合使用示例:一个简单的状态机
sealed class State {
    object Idle : State()
    data class Loading(val progress: Int) : State()  // OOP: 数据类
    data class Success(val data: String) : State()
    data class Error(val exception: Throwable) : State()
}
 
class StateMachine {
    private var currentState: State = State.Idle  // 命令式: 可变状态
    
    // 函数式: 使用 when 表达式和模式匹配
    fun transition(event: Event): State {
        currentState = when (currentState) {
            is State.Idle -> when (event) {
                is Event.Start -> State.Loading(0)
                else -> currentState
            }
            is State.Loading -> when (event) {
                is Event.Progress -> State.Loading(event.percent)
                is Event.Complete -> State.Success(event.data)
                is Event.Fail -> State.Error(event.error)
                else -> currentState
            }
            else -> currentState
        }
        return currentState
    }
}

📝 练习题

题目1: 以下代码的输出是什么?

Kotlin
val numbers = listOf(1, 2, 3, 4, 5)
val result = numbers
    .filter { it % 2 == 1 }
    .map { it * 2 }
    .sum()
println(result)

A. 15

B. 18

C. 30

D. 9

【答案】B

【解析】执行流程:filter { it % 2 == 1 } 保留奇数 [1, 3, 5]map { it * 2 } 变为 [2, 6, 10]sum() 求和 2 + 6 + 10 = 18。这是函数式编程中典型的链式调用(method chaining)模式。


题目2: Kotlin 被称为多范式语言,以下哪项不是其支持的编程范式?

A. 面向对象编程(Object-Oriented Programming)

B. 函数式编程(Functional Programming)

C. 逻辑编程(Logic Programming)

D. 命令式编程(Imperative Programming)

【答案】C

【解析】逻辑编程(Logic Programming)是 Prolog 等语言的特性,基于逻辑规则和推理。Kotlin 不直接支持这种范式。Kotlin 融合的三大范式是:面向对象(类、继承、多态)、函数式(高阶函数、Lambda、不可变性)、命令式(循环、可变状态、控制流)。


空安全哲学(Null Safety Philosophy)

十亿美元的错误(The Billion Dollar Mistake)

1965年,Tony Hoare 发明了空引用(null reference)。多年后,他公开道歉:

"I call it my billion-dollar mistake. It has caused innumerable errors, vulnerabilities, and system crashes." — Tony Hoare, 2009

Kotlin
┌─────────────────────────────────────────────────────────────────┐
│                 NullPointerException 的代价                      │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│   • Android 应用崩溃的 #1 原因                                    │
│   • 生产环境 bug 的主要来源之一                                    │
│   • 开发者需要大量防御性代码                                       │
│   • 代码审查时容易遗漏                                            │
│                                                                 │
│   典型的 Java 防御代码:                                          │
│   ┌─────────────────────────────────────────────────────────┐   │
│   │ if (user != null) {                                     │   │
│   │     if (user.getAddress() != null) {                    │   │
│   │         if (user.getAddress().getCity() != null) {      │   │
│   │             return user.getAddress().getCity().getName();│   │
│   │         }                                               │   │
│   │     }                                                   │   │
│   │ }                                                       │   │
│   │ return "Unknown";                                       │   │
│   └─────────────────────────────────────────────────────────┘   │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

可空类型的意义(The Meaning of Nullable Types)

Kotlin 的解决方案:将可空性编码到类型系统中(Encode nullability in the type system)

Kotlin
// 类型系统区分可空与非空
var name: String = "Kotlin"    // 非空类型(Non-null type)
var nickname: String? = null   // 可空类型(Nullable type)
 
// name = null   // ❌ 编译错误!Type mismatch
nickname = null  // ✅ 允许

"The type system distinguishes between references that can hold null (nullable references) and those that cannot (non-null references)." — Kotlin 官方文档

Kotlin
┌─────────────────────────────────────────────────────────────────┐
│                Kotlin 的类型层次(简化版)                         │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│                         Any?                                    │
│                    (可空的顶层类型)                                │
│                          │                                      │
│           ┌──────────────┴──────────────┐                       │
│           │                             │                       │
│          Any                          null
│    (非空的顶层类型)                   (唯一值)                     │
│           │                                                     │
│     ┌─────┴─────┐                                               │
│     │           │                                               │
│  String       Int        ...                                    │
│     │           │                                               │
│  String?      Int?       ...(加 ? 变为可空版本)                  │
│                                                                 │
│                       Nothing                                   │
│                  (所有类型的子类型)                                │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

空安全操作符(Null Safety Operators)

Kotlin 提供了一套优雅的操作符来处理可空类型:

Kotlin
val user: User? = getUser()
 
// 1. 安全调用操作符(Safe Call Operator)?.
val cityName = user?.address?.city?.name  // 任一环节为 null 则返回 null
 
// 2. Elvis 操作符(Elvis Operator)?:
val displayName = user?.name ?: "Anonymous"  // null 时使用默认值
 
// 3. 非空断言(Non-null Assertion)!!
val length = user!!.name.length  // ⚠️ 危险!null 时抛 NPE
 
// 4. 安全类型转换(Safe Cast)as?
val str: String? = value as? String  // 转换失败返回 null 而非抛异常
 
// 5. let 作用域函数
user?.let { u ->
    // 这个块内 u 是非空的
    println("User: ${u.name}, Age: ${u.age}")
}

对比 Java 的防御代码:

Kotlin
// Kotlin:优雅简洁
val cityName = user?.address?.city?.name ?: "Unknown"
 
// 等价的 Java 代码
String cityName;
if (user != null && user.getAddress() != null 
    && user.getAddress().getCity() != null) {
    cityName = user.getAddress().getCity().getName();
} else {
    cityName = "Unknown";
}

平台类型(Platform Types)

当 Kotlin 调用 Java 代码时,编译器无法知道 Java 返回值是否可能为 null。这时会出现平台类型(Platform Types)

Kotlin
// Java 方法返回 String(可能为 null)
// public String getName() { return name; }
 
val name = javaObject.getName()  // 类型是 String!(平台类型)
 
// String! 表示"可能为 null,也可能不为 null"
// 编译器不强制检查,但运行时可能 NPE
 
// 最佳实践:在边界处明确类型
val safeName: String? = javaObject.getName()  // 明确声明为可空
val nonNullName: String = javaObject.getName() ?: "default"
Kotlin
┌─────────────────────────────────────────────────────────────────┐
│              处理平台类型的最佳实践                                │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  Java 返回值  ──────►  Platform Type (String!)                  │
│                              │                                  │
│              ┌───────────────┼───────────────┐                  │
│              ▼               ▼               ▼                  │
│         视为非空         视为可空         使用 Elvis             │
val s: String    val s: String?   val s = x ?: ""
│         (危险!)         (安全)           (安全)                 │
│                                                                 │
│  推荐:在调用 Java API 时,总是假设返回值可能为 null
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

空安全的实践智慧

Kotlin
// ✅ 好的实践
class UserService {
    // 返回类型明确表达语义
    fun findUser(id: String): User? = database.find(id)  // 可能找不到
    
    fun getUser(id: String): User =                      // 保证存在
        findUser(id) ?: throw UserNotFoundException(id)
}
 
// ✅ 使用 requireNotNull / checkNotNull
fun process(data: String?) {
    val nonNullData = requireNotNull(data) { "Data cannot be null" }
    // 后续 nonNullData 是非空的
}
 
// ❌ 避免过度使用 !!
fun bad(user: User?) {
    val name = user!!.name  // 如果 null 会崩溃,且错误信息不明确
}
 
// ✅ 使用 ?.let 或 if-check
fun good(user: User?) {
    user?.let { println(it.name) }
    // 或
    if (user != null) {
        println(user.name)  // 智能转换,user 在这里是非空的
    }
}

📝 练习题

题目1: 以下代码的输出是什么?

Kotlin
val name: String? = null
val length = name?.length ?: -1
println(length)

A. null

B. 0

C. -1

D. 抛出 NullPointerException

【答案】C


类型推断机制(Type Inference Mechanism)

Kotlin 拥有强大的 类型推断(Type Inference) 能力,让你写出简洁的代码而不牺牲类型安全。

"Kotlin has local type inference, meaning in many cases the type of a variable or the return type of a function can be inferred by the compiler." — Kotlin 官方文档

局部类型推断(Local Type Inference)

局部类型推断发生在局部变量、表达式、Lambda等上下文中。编译器根据初始化表达式或上下文自动推导类型。

Kotlin
// 变量类型推断(Variable Type Inference)
val name = "Kotlin"        // 推断为 String
val count = 42             // 推断为 Int
val price = 19.99          // 推断为 Double
val flag = true            // 推断为 Boolean
val list = listOf(1, 2, 3) // 推断为 List<Int>
 
// 等价于显式声明
val name: String = "Kotlin"
val count: Int = 42

推断规则图解:

Kotlin
┌─────────────────────────────────────────────────────────────────┐
│                    局部类型推断流程                               │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
val x = expression                                            │
│           │                                                     │
│           ▼                                                     │
│   ┌───────────────────┐                                         │
│   │ 分析表达式的类型   │                                         │
│   └─────────┬─────────┘                                         │
│             │                                                   │
│       ┌─────┴─────┐                                             │
│       ▼           ▼                                             │
│   字面量       函数返回值                                         │
"text"→String   listOf(1,2)→List<Int>
42→Int          mapOf("a" to 1)→Map<String,Int>
3.14→Double     user.getName()→String (或 String?)            │
│                                                                 │
│   最终:x 的类型 = 表达式的类型                                    │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

Lambda 中的类型推断:

Kotlin
// 完整写法
val double: (Int) -> Int = { x: Int -> x * 2 }
 
// 推断参数类型(从左侧函数类型推断)
val double: (Int) -> Int = { x -> x * 2 }
 
// 推断整个 Lambda 类型(从右侧表达式推断)
val double = { x: Int -> x * 2 }  // 推断为 (Int) -> Int
 
// 使用 it(单参数 Lambda 的隐式名称)
val double: (Int) -> Int = { it * 2 }
 
// 链式调用中的推断
listOf(1, 2, 3)
    .filter { it > 1 }      // it 推断为 Int
    .map { it.toString() }  // it 推断为 Int,返回 List<String>
    .joinToString()         // 推断为 String

复杂推断示例:

Kotlin
// 泛型类型推断(Generic Type Inference)
val map = mutableMapOf<String, MutableList<Int>>()
map["numbers"] = mutableListOf()  // 值的类型从 map 的类型推断
 
// 推断链
val result = users
    .filter { it.age > 18 }           // List<User>
    .groupBy { it.city }              // Map<String, List<User>>
    .mapValues { it.value.size }      // Map<String, Int>
    .maxByOrNull { it.value }         // Map.Entry<String, Int>?
    ?.key                             // String?

全局类型推断的边界(Limits of Type Inference)

Kotlin 使用的是局部类型推断(Local Type Inference)而非全局类型推断(Global Type Inference)。这意味着某些场景下必须显式声明类型

Kotlin
┌─────────────────────────────────────────────────────────────────┐
│              必须显式声明类型的场景                                │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
1. 公开 API 的返回类型(Public API Return Types)               │
2. 未初始化的变量(Uninitialized Variables)                    │
3. 递归函数(Recursive Functions)                              │
4. 某些复杂泛型场景(Complex Generic Scenarios)                 │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

场景1:公开 API 必须声明返回类型

Kotlin
// ✅ private 函数可以推断返回类型
private fun calculateSum(a: Int, b: Int) = a + b  // 推断为 Int
 
// ❌ public 函数必须显式声明返回类型(最佳实践)
// 虽然编译器允许推断,但官方强烈建议显式声明
public fun calculateSum(a: Int, b: Int): Int = a + b
 
// 原因:公开 API 的返回类型是契约的一部分
// 显式声明可以:
//   - 作为文档
//   - 防止意外更改 API
//   - 提高代码可读性

场景2:延迟初始化的变量

Kotlin
// ❌ 编译错误:必须声明类型或初始化
val name  // Error: This variable must either have a type annotation or be initialized
 
// ✅ 方案1:提供初始值
val name = "default"
 
// ✅ 方案2:显式声明类型
val name: String
 
// ✅ 方案3:lateinit(仅用于 var 和非基本类型)
lateinit var name: String
 
// ✅ 方案4:lazy 委托
val name: String by lazy { computeName() }

场景3:递归函数必须声明返回类型

Kotlin
// ❌ 编译错误:递归调用需要显式返回类型
fun factorial(n: Int) = if (n <= 1) 1 else n * factorial(n - 1)
//                                              ↑ Error!
 
// ✅ 正确:显式声明返回类型
fun factorial(n: Int): Int = if (n <= 1) 1 else n * factorial(n - 1)
 
// ✅ 或者使用代码块形式
fun factorial(n: Int): Int {
    return if (n <= 1) 1 else n * factorial(n - 1)
}

场景4:复杂泛型需要帮助编译器

Kotlin
// 编译器可能无法推断某些复杂场景
val emptyList = listOf()  // ❌ 类型是 List<Nothing>,可能不是你想要的
 
// ✅ 显式指定类型参数
val emptyList = listOf<String>()
val emptyList: List<String> = listOf()
val emptyList = emptyList<String>()
 
// 构建器模式可能需要类型帮助
val map = buildMap {      // ❌ 可能推断失败
    put("a", 1)
}
 
val map = buildMap<String, Int> {  // ✅ 显式指定
    put("a", 1)
}

类型推断的最佳实践(Best Practices)

Kotlin
// ✅ DO: 局部变量充分利用类型推断
fun processData() {
    val name = getName()              // 简洁
    val users = fetchUsers()          // 清晰
    val result = users.filter { it.active }
}
 
// ✅ DO: 公开 API 显式声明类型
class UserRepository {
    fun findById(id: Long): User? { ... }      // 明确返回可空
    fun getAll(): List<User> { ... }           // 明确返回列表
    fun save(user: User): User { ... }         // 明确返回保存后的实体
}
 
// ⚠️ CONSIDER: 复杂表达式考虑加类型注解增加可读性
val usersByCity: Map<String, List<User>> = users.groupBy { it.city }
 
// ❌ AVOID: 过度使用显式类型(噪音)
val name: String = "Kotlin"           // 冗余
val count: Int = 42                   // 冗余
val list: List<Int> = listOf(1,2,3)   // 冗余
Kotlin
┌─────────────────────────────────────────────────────────────────┐
│                  类型推断决策树                                   │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│                    需要声明类型吗?                               │
│                          │                                      │
│            ┌─────────────┼─────────────┐                        │
│            ▼             ▼             ▼                        │
│        公开 API?      延迟初始化?     递归函数?                    │
│            │             │             │                        │
│     Yes→显式声明   Yes→显式声明   Yes→显式声明                     │
│            │             │             │                        │
│            └──────┬──────┴─────────────┘                        │
│                   ▼                                             │
│              局部变量?                                          │
│                   │                                             │
│         ┌────────┴────────┐                                     │
│         ▼                 ▼                                     │
│    初始值清晰?      表达式复杂?                                  │
│         │                 │                                     │
│    Yes→省略类型      Yes→考虑加类型注解                            │
│                      (增加可读性)                                 │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

📝 练习题

题目1: 以下哪种情况 Kotlin 编译器无法自动推断类型?

A. val name = "Kotlin" B. val list = listOf(1, 2, 3) C. fun factorial(n: Int) = if (n <= 1) 1 else n * factorial(n - 1) D. val sum = { a: Int, b: Int -> a + b }

【答案】C

【解析】递归函数(Recursive Function)必须显式声明返回类型。因为编译器在推断 factorial 返回类型时需要知道 factorial(n-1) 的返回类型,形成循环依赖。正确写法是 fun factorial(n: Int): Int = ...。其他选项都能正常推断:A 是字符串字面量,B 是泛型函数调用,D 是 Lambda 表达式(参数类型已提供)。


题目2: 关于 Kotlin 类型推断,以下说法正确的是:

A. Kotlin 使用全局类型推断,可以推断任何位置的类型

B. 公开 API 的返回类型可以完全依赖推断,无需声明

C. 局部变量推荐利用类型推断简化代码

D. Lambda 参数必须显式声明类型

【答案】C

【解析】Kotlin 使用局部类型推断(Local Type Inference),推荐在局部变量中充分利用以简化代码。A 错误,Kotlin 不是全局推断;B 错误,公开 API 建议显式声明返回类型作为契约和文档;D 错误,Lambda 参数类型通常可以从上下文推断,如 list.map { it * 2 } 中的 it 类型由 list 元素类型推断得出。


本章小结(Chapter Summary)

Kotlin
┌─────────────────────────────────────────────────────────────────┐
│                                                                 │
│                   第一章 Kotlin 入门                             │
│                   Chapter 1: Getting Started
│                                                                 │
│                        知识地图                                  │
│                     Knowledge Map                               │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

        ┌─────────────────────┼─────────────────────┐
        ▼                     ▼                     ▼
   ┌─────────┐          ┌─────────┐          ┌─────────┐
   │语言哲学  │          │历史发展  │          │基本概念  │
   │Philosophy│          │History  │          │Concepts │
   └────┬────┘          └────┬────┘          └────┬────┘
        │                    │                    │
   • 简洁性             • JetBrains         • 表达式优先
     Conciseness          2010 开始            Expression-
   • 安全性             • 2016 v1.0            oriented
     Safety             • 2017 Android      • 语句 vs 表达式
   • 实用主义             首选语言             Statement vs
     Pragmatism        • 2024 K2 编译器        Expression
                                            • 不可变性
                                              Immutability
        │                     │                    │
        └─────────────────────┼────────────────────┘

        ┌─────────────────────┼─────────────────────┐
        ▼                     ▼                     ▼
   ┌─────────┐          ┌─────────┐          ┌─────────┐
   │Java互操作│         │  多范式  │          │  空安全  │
   │Interop  │          │Paradigms│          │Null Safe│
   └────┬────┘          └────┬────┘          └────┬────┘
        │                    │                    │
100%兼容           • 面向对象 OOP       • 十亿美元错误
   • 渐进迁移           • 函数式 FP          • ?. ?:  !!
   • 共存策略           • 命令式             • 平台类型
@Jvm注解           Imperative          Platform Types


                       ┌─────────────┐
                       │  类型推断    │
                       │  Inference  │
                       └──────┬──────┘

                        • 局部推断
                        • 推断边界
                        • 最佳实践

关键语法速查(Quick Reference)

Kotlin
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// 变量声明 Variable Declaration
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
val immutable = "不可重新赋值"    // Immutable reference
var mutable = "可以重新赋值"      // Mutable reference
 
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// 空安全 Null Safety
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
val nullable: String? = null     // 可空类型
val safe = nullable?.length      // 安全调用 Safe call
val default = nullable ?: "N/A"  // Elvis 操作符
val forced = nullable!!          // 非空断言(慎用!)
 
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// 表达式 Expressions
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
val max = if (a > b) a else b    // if 表达式
 
val grade = when (score) {       // when 表达式
    in 90..100 -> "A"
    in 80..89  -> "B"
    else       -> "C"
}
 
val result = try {               // try 表达式
    riskyOperation()
} catch (e: Exception) {
    defaultValue
}
 
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// 函数式编程 Functional Programming
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
val doubled = list.map { it * 2 }
val filtered = list.filter { it > 0 }
val sum = list.reduce { acc, x -> acc + x }
 
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// 数据类 Data Class
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
data class User(val name: String, val age: Int)
val user = User("Alice", 25)
val copy = user.copy(age = 26)   // 创建修改后的副本

思维转变(Mindset Shift)

Kotlin
┌─────────────────────────────────────────────────────────────────┐
│                  从 Java 到 Kotlin 的思维转变                     │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│   Java 思维                      Kotlin 思维                     │
│   ─────────                      ──────────                     │
│                                                                 │
"我需要写 getter/setter""用属性和 data class"
│                                                                 │
"null 检查是我的责任""类型系统帮我检查 null"
│                                                                 │
"需要 if-else 赋值临时变量""if 是表达式,直接返回值"
│                                                                 │
"写个工具类加静态方法""写扩展函数,更自然"
│                                                                 │
"回调嵌套处理异步""协程让异步像同步"
│                                                                 │
"继承一切""组合优于继承,接口委托"
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

📝 综合练习

题目1: 综合本章内容,以下代码体现了 Kotlin 的哪些特性?

Kotlin
data class User(val name: String, val email: String?)
 
fun User.displayName() = name.uppercase()
 
fun main() {
    val user = User("alice", null)
    println(user.displayName())           // ALICE
    println(user.email?.length ?: 0)      // 0
}

A. 只有数据类

B. 数据类 + 空安全

C. 数据类 + 空安全 + 扩展函数 + 类型推断

D. 只有扩展函数和空安全

【答案】C

【解析】这段代码展示了多个 Kotlin 特性:

  • 数据类(Data Class)data class User 自动生成 equals/hashCode/toString

  • 空安全(Null Safety)email: String? 声明可空,?. 安全调用,?: Elvis 操作符

  • 扩展函数(Extension Function)fun User.displayName() 为 User 类添加方法

  • 类型推断(Type Inference)val user = ... 自动推断为 User 类型 这正是 Kotlin 多特性协同工作的典型示例。


题目2: 根据 Kotlin 的设计哲学,以下哪种做法最符合"实用主义(Pragmatism)"原则?

A. 强制所有项目立即从 Java 完全迁移到 Kotlin

B. 设计一套全新的构建系统替代 Gradle

C. 保持与 Java 100% 互操作,支持渐进式采用

D. 只支持函数式编程,禁止使用可变状态

【答案】C

【解析】实用主义(Pragmatism)的核心是解决实际问题而非追求理论完美。Kotlin 选择与 Java 100% 互操作,让团队可以:

  • 在现有 Java 项目中逐步引入 Kotlin

  • 新旧代码无缝协作

  • 复用整个 Java 生态系统

这种务实的设计降低了采用门槛,是 Kotlin 成功的关键因素之一。A/B/D 都是"推倒重来"的激进做法,与实用主义相悖。