Kotlin 核心特性

小组内部技术分析会

Phase 1 + Phase 2

空安全 · 数据类 · 密封类 · 协程

目录

01空安全体系
02数据类 data class
03密封类/接口
04值类型 value class
05集合
06高阶函数
07内联函数 inline
08扩展函数
09泛型 in/out/reified
10委托 by
11协程基础构建器
12协程作用域
13调度器 Dispatcher
14协程上下文 Context
15Job 与子协程
16异常处理 + 并发 + Flow

空安全体系

从类型系统层面杜绝 NPE

可空类型 / 非空类型

var normalStr: String = "Hello"    // 非空
var nullableStr: String? = null    // 可空

安全操作符

val len = str?.length       // ?. null 短路
val realStr = str ?: "默认"   // ?: 为空默认值
val length = str!!.length    // !! 强行断言(慎用)
val len = str?.length ?: 0   // 组合(推荐)

空安全体系 · 实战

空安全 + 函数返回

fun getUserName(): String? = null
fun login() {
    val name = getUserName() ?: return
    println("用户名合法:$name")
}

平台类型(Java 坑点)

Kotlin 调用 Java 代码时,平台类型可空性模糊,容易 NPE
val name = JavaUtil.getName() // 编译器不强制判空

数据类 data class

专为纯数据载体设计

自动生成四大方法

data class User(val id: Long, val name: String, val age: Int)
val u1 = User(1, "张三", 20)
val u2 = User(1, "张三", 20)
println(u1 == u2)  // true - 自动 equals

解构 + copy

val (id, name, age) = User(10, "李四", 25)
val newUser = oldUser.copy(age = 31)

数据类 · 实战

嵌套数据类(接口响应封装)

data class ApiResponse(val code: Int, val msg: String, val data: T?)
data class LoginData(val token: String, val userId: Long)

约束规则

  • 主构造函数至少有一个参数
  • 参数用 val/var 声明
  • 不能被 open 继承、不能抽象
  • 类体内属性不参与 equals/toString

密封类/接口 sealed class

when 穷尽分支,可携带不同数据

密封类基础

sealed class PageState {
    object Loading : PageState()
    data class Success(val data: T) : PageState()
    data class Error(val msg: String, val code: Int) : PageState()
}
when (state) {
    is PageState.Loading -> println("加载中")
    is PageState.Success<*> -> println("成功:${state.data}")
    is PageState.Error -> println("失败:${state.code}")
}

密封抽象类 + 密封对象

sealed abstract class Result { abstract val message: String }
sealed object AppEvent { object LoginExpire }

枚举 vs 密封类

选型对比

场景推荐
固定常量、不需要携带数据枚举 Enum
每个分支携带不同类型数据密封类 Sealed
需要 when 强穷尽校验密封类 Sealed

Android 常用模板

sealed class ApiResult {
    object Loading : ApiResult()
    data class Success(val data: T) : ApiResult()
    data class Error(val msg: String) : ApiResult()
}

值类型 value class

编译期解包,零开销,强类型

基础用法

value class UserId(val id: Long)  // 编译后等价 Long
value class Email(val value: String)

vs data class vs typealias

data class UserIdData(val id: Long)  // 有对象开销
typealias OrderId = Long              // 无类型安全
value class OrderId(val id: Long)     // 零开销 + 强类型

集合

不可变 vs 可变

val list: List = listOf(1,2,3)    // 只读
val mList = mutableListOf(1,2,3)        // 可增删

普通 List vs Sequence

List 每步生成新集合,Sequence 惰性求值无中间集合
list.filter{}.map{}.take(2)          // 立即求值
list.asSequence().filter{}.map{}.toList()  // 惰性求值

高阶函数

把函数当参数 / 返回值

函数类型

val func0: () -> Unit
val func1: (Int) -> Int
val func2: (String, Int) -> Boolean

高阶函数示例

fun calculate(a: Int, b: Int, op: (Int, Int) -> Int) = op(a, b)
val sum = calculate(2, 3) { x, y -> x + y }  // 5

五大作用域函数

函数内部指代返回值适用场景
letitlambda 结果判空、类型转换
runthislambda 结果初始化并返回计算值
applythis原对象对象配置、初始化
alsoit原对象插日志、链式中间处理
withthislambda 结果同一对象批量操作

内联函数 inline fun

编译代码拷贝,消除 Lambda 开销

inline 解决

inline fun inlineHigherFunc(block: () -> Unit) {
    block()  // 编译后代码直接拷贝到调用处
}
// 无函数调用、无匿名类

noinline / crossinline

  • noinline:某个 Lambda 不内联,可赋值传递
  • crossinline:嵌套作用域调用,禁止非局部 return
  • inline 支持非局部 return

扩展函数

不修改原类源码,新增方法

语法

fun 接收者类.方法名(参数): 返回值 { }

示例

fun String.isBlankExt() = this.isEmpty() || this.isBlank()
fun User.getDesc() = "用户名:$name"
fun  T.printSelf(): T { println(this); return this }

重要:静态解析,不支持多态

fun Animal.say() = "动物叫声"
val cat: Animal = Cat()
println(cat.say()) // 动物叫声!走 Animal 的扩展

泛型 in/out/reified

out 协变(生产者)

interface Producer { fun produce(): T }
val producer: Producer = AppleProducer()

in 逆变(消费者)

interface Consumer { fun consume(t: T) }
val consumer: Consumer = FruitConsumer()

reified 泛型实化

解决 Java 泛型擦除问题,必须配合 inline 使用
inline fun  checkType(value: Any) {
    if (value is T) println("是 ${T::class.simpleName} 类型")
}

委托 by 关键字

替代继承,减少样板代码

类委托

interface ISpeak { fun speak() }
class Puppy(delegate: ISpeak) : ISpeak by delegate
puppy.speak() // 狗狗汪汪叫

属性委托

val lazyStr: String by lazy { "Hello" }        // 延迟初始化
var count: Int by Delegates.observable(0) { _, old, new -> println("$old -> $new") }
var age: Int by Delegates.vetoable(18) { _, _, new -> new > 0 }

协程基础构建器

runBlocking vs GlobalScope

runBlocking(阻塞当前线程)

  • 阻塞当前线程,直到内部所有协程执行完
  • 用于:main 函数测试、单元测试
  • 严禁在 Android 主线程业务滥用
runBlocking { delay(1000); println("协程结束") }
println("main 结束")  // 等待协程执行完才打印

GlobalScope(全局作用域)⚠️ 禁止使用

生命周期跟 App 全程一致,无法自动取消,容易内存泄漏
GlobalScope.launch { delay(2000); println("完成") }
println("main 立刻结束")  // 主线程直接退出,协程被杀

协程作用域 CoroutineScope

结构化并发,生命周期管控

核心特性

  • 协程都必须跑在某个 CoroutineScope 里
  • 作用域取消 → 内部所有子协程全部自动取消
  • 父子协程生命周期绑定,杜绝内存泄漏

模拟 viewModelScope 原理

class MyViewModel {
    private val viewModelJob = SupervisorJob()
    val viewModelScope = CoroutineScope(viewModelJob + Dispatchers.Main)
    fun onCleared() { viewModelJob.cancel() }
}

总结

  • viewModelScope:Dispatchers.Main,ViewModel 销毁自动 cancel
  • lifecycleScope:绑定 Activity/Fragment 生命周期
  • 禁止用 GlobalScope:用受控作用域

调度器 CoroutineDispatcher

调度器用途适用场景
Dispatchers.Main主线程更新 UI
Dispatchers.IOIO 阻塞任务网络、数据库、文件、SP
Dispatchers.DefaultCPU 计算任务大数据排序、解析、算法
Dispatchers.Unconfined不绑定线程框架底层用,业务尽量不用

线程切换经典写法(Android 标准模板)

suspend fun netRequest(): String {
    return withContext(Dispatchers.IO) {
        delay(1000)
        "接口返回数据"
    }
}
launch(Dispatchers.Main) {
    val result = netRequest()  // 自动切回主线程
    println("更新UI: $result")
}

协程上下文 CoroutineContext

常见元素

  • Dispatcher:调度器(线程)
  • Job:协程生命周期、取消、父子关系
  • CoroutineName:协程名称,调试日志用
  • CoroutineExceptionHandler:异常捕获处理器

创建带多元素的上下文

val myContext = Dispatchers.IO + CoroutineName("MyCoroutine")
launch(myContext) {
    println(coroutineContext[CoroutineName]?.name)
}

上下文继承规则

子协程默认继承父协程的 CoroutineContext,只可局部覆盖某一个元素
launch(Dispatchers.IO) {  // 只覆盖调度器,名字仍继承父级
    println(coroutineContext[CoroutineName]?.name)
}

withContext 切换上下文原理

withContext(Dispatchers.IO) {  // 只替换调度器,其他元素全部继承
    // 执行完自动切回原上下文
}

Job 与 子协程

普通 Job:父取消,所有子协程全部取消

val parentJob = launch {
    launch { /* 子协程1 */ }
    launch { /* 子协程2 */ }
}
parentJob.cancel()  // 父取消 → 所有子全部停止

普通 Job:子异常 → 父 + 其他子全部崩溃

普通 Job 异常向上传播,一子翻车,全队陪葬

SupervisorJob:子崩溃不传染

val supervisorScope = CoroutineScope(SupervisorJob())
supervisorScope.launch {
    launch { /* 子1 正常跑 */ }
    launch { throw RuntimeException("子2崩") }  // 子2崩,子1不受影响
}

supervisorScope 简化写法

supervisorScope {
    launch { /* 子1 */ }
    launch { throw RuntimeException("子2异常") }
}
// 一个子崩,其他子正常执行

异常处理 + 并发 + Flow

async + await 并发

val deferred1 = async { fetchUser() }
val deferred2 = async { fetchPosts() }
val user = deferred1.await()
val posts = deferred2.await()

// 官方推荐:withContext 替代 async
val user = withContext(Dispatchers.IO) { fetchUser() }

异常处理

  • CoroutineExceptionHandler - 全局捕获
  • supervisorScope - 监督作用域
  • 子协程崩溃会传播给父协程

Flow 流

  • 冷流 - 不订阅不执行
  • 热流 SharedFlow / StateFlow
  • 操作符:map、filter、catch、onCompletion
  • Kotlin 官方响应式编程方案

取消与超时

job.cancel()
withTimeout(3000) { /* 超时自动取消 */ }
withTimeoutOrNull(3000) { /* 超时返回 null */ }
1 / 22