小组内部技术分析会
空安全 · 数据类 · 密封类 · 协程
从类型系统层面杜绝 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")
}
val name = JavaUtil.getName() // 编译器不强制判空
专为纯数据载体设计
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
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)
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 }
| 场景 | 推荐 |
|---|---|
| 固定常量、不需要携带数据 | 枚举 Enum |
| 每个分支携带不同类型数据 | 密封类 Sealed |
| 需要 when 强穷尽校验 | 密封类 Sealed |
sealed class ApiResult {
object Loading : ApiResult()
data class Success(val data: T) : ApiResult()
data class Error(val msg: String) : ApiResult()
}
编译期解包,零开销,强类型
value class UserId(val id: Long) // 编译后等价 Long
value class Email(val value: String)
data class UserIdData(val id: Long) // 有对象开销
typealias OrderId = Long // 无类型安全
value class OrderId(val id: Long) // 零开销 + 强类型
val list: List = listOf(1,2,3) // 只读
val mList = mutableListOf(1,2,3) // 可增删
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
| 函数 | 内部指代 | 返回值 | 适用场景 |
|---|---|---|---|
| let | it | lambda 结果 | 判空、类型转换 |
| run | this | lambda 结果 | 初始化并返回计算值 |
| apply | this | 原对象 | 对象配置、初始化 |
| also | it | 原对象 | 插日志、链式中间处理 |
| with | this | lambda 结果 | 同一对象批量操作 |
编译代码拷贝,消除 Lambda 开销
inline fun inlineHigherFunc(block: () -> Unit) {
block() // 编译后代码直接拷贝到调用处
}
// 无函数调用、无匿名类
不修改原类源码,新增方法
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 的扩展
interface Producer { fun produce(): T }
val producer: Producer = AppleProducer()
interface Consumer { fun consume(t: T) }
val consumer: Consumer = FruitConsumer()
inline fun checkType(value: Any) {
if (value is T) println("是 ${T::class.simpleName} 类型")
}
替代继承,减少样板代码
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 { delay(1000); println("协程结束") }
println("main 结束") // 等待协程执行完才打印
GlobalScope.launch { delay(2000); println("完成") }
println("main 立刻结束") // 主线程直接退出,协程被杀
结构化并发,生命周期管控
class MyViewModel {
private val viewModelJob = SupervisorJob()
val viewModelScope = CoroutineScope(viewModelJob + Dispatchers.Main)
fun onCleared() { viewModelJob.cancel() }
}
| 调度器 | 用途 | 适用场景 |
|---|---|---|
| Dispatchers.Main | 主线程 | 更新 UI |
| Dispatchers.IO | IO 阻塞任务 | 网络、数据库、文件、SP |
| Dispatchers.Default | CPU 计算任务 | 大数据排序、解析、算法 |
| Dispatchers.Unconfined | 不绑定线程 | 框架底层用,业务尽量不用 |
suspend fun netRequest(): String {
return withContext(Dispatchers.IO) {
delay(1000)
"接口返回数据"
}
}
launch(Dispatchers.Main) {
val result = netRequest() // 自动切回主线程
println("更新UI: $result")
}
val myContext = Dispatchers.IO + CoroutineName("MyCoroutine")
launch(myContext) {
println(coroutineContext[CoroutineName]?.name)
}
launch(Dispatchers.IO) { // 只覆盖调度器,名字仍继承父级
println(coroutineContext[CoroutineName]?.name)
}
withContext(Dispatchers.IO) { // 只替换调度器,其他元素全部继承
// 执行完自动切回原上下文
}
val parentJob = launch {
launch { /* 子协程1 */ }
launch { /* 子协程2 */ }
}
parentJob.cancel() // 父取消 → 所有子全部停止
val supervisorScope = CoroutineScope(SupervisorJob())
supervisorScope.launch {
launch { /* 子1 正常跑 */ }
launch { throw RuntimeException("子2崩") } // 子2崩,子1不受影响
}
supervisorScope {
launch { /* 子1 */ }
launch { throw RuntimeException("子2异常") }
}
// 一个子崩,其他子正常执行
val deferred1 = async { fetchUser() }
val deferred2 = async { fetchPosts() }
val user = deferred1.await()
val posts = deferred2.await()
// 官方推荐:withContext 替代 async
val user = withContext(Dispatchers.IO) { fetchUser() }
job.cancel()
withTimeout(3000) { /* 超时自动取消 */ }
withTimeoutOrNull(3000) { /* 超时返回 null */ }