Kotlin 深入解析

FreeGuideOnline 最新 2026-07-14

kotlin var name: String = "Kotlin" // name = null // 编译错误


- **可空类型**:在类型后添加 `?` 表示可空,使用时必须显式处理空值。
```kotlin
var nullableName: String? = "Hello"
nullableName = null
  • 安全调用 ?.:当对象不为 null 时才执行方法,否则直接返回 null
val length = nullableName?.length  // 类型为 Int?
  • Elvis 操作符 ?::提供默认值。
val len = nullableName?.length ?: 0
  • 非空断言 !!:强行将可空类型当作非空使用,若为 null 则抛出异常,应谨慎使用。
val forceLen = nullableName!!.length
  • 平台类型 hint:与 Java 互操作时,来自 Java 的引用为“平台类型”,可选择作为可空或非空处理,建议尽早明确其空性。

扩展函数与扩展属性

Kotlin 允许在不修改原有类的情况下,为类增添新功能。

  • 定义扩展函数:fun 类名.函数名()
fun String.lastChar(): Char = this[this.length - 1]
println("Kotlin".lastChar())  // n
  • 扩展属性:只能声明为 val,且不能持有状态,需通过 getter 实现。
val String.firstChar: Char get() = this[0]
  • 扩展的本质是静态方法调用,调用时由左侧变量声明的类型决定使用哪个扩展函数(静态解析),而非动态分发。

  • 成员函数优先级高于扩展函数;扩展函数可以覆盖,但仅当对象类型为扩展定义的接收者类型时才会调用扩展版本。

数据类:自动生成通用方法

数据类是为保存数据而生,编译器自动生成 equals()hashCode()toString()copy()componentN() 函数。

data class Person(val name: String, val age: Int)

val p1 = Person("Alice", 30)
val p2 = p1.copy(age = 31)
println(p1)  // Person(name=Alice, age=30)

// 解构声明
val (n, a) = p1

要求主构造函数至少有一个参数,且参数必须用 valvar 声明。数据类不能被继承,但可实现接口。

密封类与密封接口

密封类用于表示受限的类层次结构,所有子类必须在同一个文件中定义(密封接口可在同一模块内)。

sealed class Result
data class Success(val data: String) : Result()
data class Error(val message: String) : Result()
object Loading : Result()

fun handle(result: Result) = when (result) {
    is Success -> println(result.data)
    is Error -> println(result.message)
    is Loading -> println("Loading...")
    // 不需要 else 分支,编译器检查完备
}

密封类配合 when 表达式可实现穷举检查,保证分支覆盖的完整性,是状态管理的利器。

对象声明与伴生对象

  • 对象声明:单例模式的最简实现。
object Logger {
    fun log(msg: String) = println("[LOG] $msg")
}
Logger.log("Hello")
  • 伴生对象:在类内部使用 companion object,相当于 Java 的静态成员容器,可继承或实现接口。
class MyClass {
    companion object Factory {
        fun create(): MyClass = MyClass()
    }
}
val instance = MyClass.create()

Lambda 表达式与高阶函数

Kotlin 支持将函数作为参数传递或返回,函数类型写法为 (参数类型) -> 返回值类型

  • Lambda 语法:{ 参数 -> 函数体 },若仅有一个参数可隐式使用 it
val double: (Int) -> Int = { it * 2 }
fun operate(x: Int, op: (Int) -> Int) = op(x)
println(operate(5, double))  // 10
  • 函数引用 ::String::length::double 等。

  • 内联函数 inline:将函数体直接展开到调用处,避免函数对象和调用的性能开销,适用于接收 lambda 作为参数的函数。可使用 noinlinecrossinline 控制内联行为。

  • 非局部返回:内联函数的 lambda 中可以使用 return 退出外层函数,非内联则需使用 return@标签

作用域函数:简洁处理对象

Kotlin 提供五大作用域函数 letrunwithapplyalso,核心区别在于上下文对象引用(thisit)和返回值(自身或 lambda 结果)。

函数 上下文引用 返回值
let it lambda 结果
run this lambda 结果
with this lambda 结果
apply this 上下文对象
also it 上下文对象

典型场景:

  • let:通常结合安全调用处理可空对象,或转换变量。
val length = "text".let { it.length }
  • apply:初始化对象时连续设置属性。
val person = Person().apply {
    name = "Bob"
    age = 25
}
  • run:既执行操作又返回结果,常用于计算值。
val result = "Kotlin".run { substring(1, 4) }
  • also:附加操作(如日志、赋值)且不改变对象本身。
val list = mutableListOf(1,2,3).also { println("list size: ${it.size}") }

委托属性与标准委托

by 关键字用于委托属性的 getter/setter 到另一个对象。

  • lazy:延迟初始化,仅在线程安全模式下计算一次。
val greeting: String by lazy { 
    println("Computed")
    "Hello"
}
  • observable:属性变化时触发回调。
var name: String by Delegates.observable("") { prop, old, new ->
    println("$old -> $new")
}
  • vetoable:可在赋值前拦截并否决更改。

  • map 委托:将属性值映射到 Map。

class User(val map: Map<String, Any?>) {
    val name: String by map
    val age: Int     by map
}
val user = User(mapOf("name" to "Alice", "age" to 25))

自定义委托需实现 ReadOnlyPropertyReadWriteProperty 接口。

协程:结构化并发

协程是 Kotlin 处理异步任务的核心工具,以轻量级线程的方式编写异步代码。

  • 挂起函数 suspend:可在不阻塞线程的情况下挂起协程。
suspend fun fetchUser(): String {
    delay(1000L) // 非阻塞延时
    return "User"
}
  • 启动协程:通常使用 launch(不关心结果)或 async(返回 Deferred 并通过 await() 获取结果)。
coroutineScope {
    val deferred = async { fetchUser() }
    println(deferred.await())
}
  • 协程上下文与调度器

    • Dispatchers.Main:Android 主线程。
    • Dispatchers.IO:IO 密集型任务。
    • Dispatchers.Default:CPU 密集型任务。
    • Dispatchers.Unconfined:不限制线程,早期测试用。
  • 作用域构建器

    • coroutineScope:创建一个新的协程作用域,当所有子协程完成时才会完成。
    • supervisorScope:一个子协程失败不会取消其他兄弟协程。
  • Flow:冷数据流,用于处理异步序列。操作符如 mapfiltercollect 等与响应式编程类似,但完全基于协程。

flow { 
    for (i in 1..3) { emit(i) }
}.map { it * 2 }
 .collect { println(it) }
  • 异常处理:使用 try-catchCoroutineExceptionHandler。结构化并发保证异常自动传播,supervisorScopeSupervisorJob 可限制传播。

反编译与 Java 互操作揭秘

理解 Kotlin 编译后的 Java 字节码有助于写出更高效的代码。

  • 顶层函数/属性编译为静态成员,所属类名为“文件名Kt”。
  • 扩展函数编译为静态方法,接收者作为第一个参数。
  • 伴生对象编译为内部静态类并持有静态引用。
  • object 声明编译为线程安全的饿汉式单例。
  • inline 函数使代码膨胀但消除函数调用开销。
  • 协程会被编译为状态机(Continuation 等),挂起点产生代码块分割。

与 Java 混合开发时,借助 @JvmStatic@JvmOverloads@JvmField 等注解可精准控制 Java 可见接口;避免在 Java 中直接使用 Kotlin 的 companion object 属性,而是使用注解暴露为静态成员。

反射与运行时操作

Kotlin 反射库提供 KClassKFunctionKProperty 等 API,实现运行时的类型探查。

val kClass = String::class
kClass.members.forEach { println(it.name) }

fun greet() { println("Hi") }
val function = ::greet
function.call()