Kotlin 深入解析
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
要求主构造函数至少有一个参数,且参数必须用 val 或 var 声明。数据类不能被继承,但可实现接口。
密封类与密封接口
密封类用于表示受限的类层次结构,所有子类必须在同一个文件中定义(密封接口可在同一模块内)。
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 作为参数的函数。可使用noinline、crossinline控制内联行为。 -
非局部返回:内联函数的 lambda 中可以使用
return退出外层函数,非内联则需使用return@标签。
作用域函数:简洁处理对象
Kotlin 提供五大作用域函数 let、run、with、apply、also,核心区别在于上下文对象引用(this 或 it)和返回值(自身或 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))
自定义委托需实现 ReadOnlyProperty 或 ReadWriteProperty 接口。
协程:结构化并发
协程是 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:冷数据流,用于处理异步序列。操作符如
map、filter、collect等与响应式编程类似,但完全基于协程。
flow {
for (i in 1..3) { emit(i) }
}.map { it * 2 }
.collect { println(it) }
- 异常处理:使用
try-catch或CoroutineExceptionHandler。结构化并发保证异常自动传播,supervisorScope或SupervisorJob可限制传播。
反编译与 Java 互操作揭秘
理解 Kotlin 编译后的 Java 字节码有助于写出更高效的代码。
- 顶层函数/属性编译为静态成员,所属类名为“文件名Kt”。
- 扩展函数编译为静态方法,接收者作为第一个参数。
- 伴生对象编译为内部静态类并持有静态引用。
object声明编译为线程安全的饿汉式单例。inline函数使代码膨胀但消除函数调用开销。- 协程会被编译为状态机(
Continuation等),挂起点产生代码块分割。
与 Java 混合开发时,借助 @JvmStatic、@JvmOverloads、@JvmField 等注解可精准控制 Java 可见接口;避免在 Java 中直接使用 Kotlin 的 companion object 属性,而是使用注解暴露为静态成员。
反射与运行时操作
Kotlin 反射库提供 KClass、KFunction、KProperty 等 API,实现运行时的类型探查。
val kClass = String::class
kClass.members.forEach { println(it.name) }
fun greet() { println("Hi") }
val function = ::greet
function.call()