// sealed 修饰符
sealed class Result {
class Success(val data: String) : Result()
class Error(val message: String) : Result()
object Loading : Result() // object 表示单例
}
使用示例
kotlin复制代码
// 定义密封类
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>()
}
// when 表达式(编译器会检查是否穷尽)
fun handleResult(result: Result<String>) {
when (result) {
is Result.Success -> println("成功: ${result.data}")
is Result.Error -> println("失败: ${result.message}")
is Result.Loading -> println("加载中...")
// 不需要 else,因为所有情况都已覆盖
}
}
密封类的特点
kotlin复制代码
sealed class Expr {
class Num(val value: Int) : Expr()
class Sum(val left: Expr, val right: Expr) : Expr()
}
// 1. 不能被外部实例化
// val e = Expr() // 错误
// 2. 子类必须在这个文件里定义
class MyExpr : Expr() // 错误:不能继承 sealed 类
// 3. 子类可以有不同的形态
val num = Expr.Num(10) // 带参数
val sum = Expr.Sum(num, Expr.Num(20)) // 嵌套
应用场景
场景一:UI 状态
kotlin复制代码
sealed class UiState<out T> {
object Loading : UiState<Nothing>()
data class Success<T>(val data: T) : UiState<T>()
data class Error(val msg: String) : UiState<Nothing>()
}
// 使用
val state: UiState<List<String>> = UiState.Success(listOf("A", "B"))
when (state) {
is UiState.Loading -> showLoading()
is UiState.Success -> showData(state.data) // data 类型已知
is UiState.Error -> showError(state.msg)
}
场景二:命令模式
kotlin复制代码
sealed class Command {
class Add(val item: String) : Command()
class Remove(val index: Int) : Command()
object Clear : Command()
}
fun execute(command: Command) {
when (command) {
is Command.Add -> addItem(command.item)
is Command.Remove -> removeItem(command.index)
is Command.Clear -> clearAll()
}
}
场景三:导航结果
kotlin复制代码
sealed class NavigationResult {
object Success : NavigationResult()
object Cancelled : NavigationResult()
data class Error(val code: Int) : NavigationResult()
}
2. 延迟初始化
为什么需要延迟初始化?
kotlin复制代码
class MyActivity : AppCompatActivity() {
private var adapter: MyAdapter // 不能直接赋值
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// adapter 必须在 setContentView() 之后才能初始化
adapter = MyAdapter() // 太晚了?或者太早了?
}
}
lateinit
kotlin复制代码
class MyActivity : AppCompatActivity() {
private lateinit var adapter: MyAdapter // 延迟初始化声明
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 在合适的时候初始化
adapter = MyAdapter()
}
fun getAdapter() = adapter // 使用时保证已初始化
}
lateinit 规则
kotlin复制代码
// 1. 只能修饰 var
lateinit var name: String // ✓
lateinit val age: Int // ✗ val 不可变
// 2. 不能用于基本类型(Int、Boolean 等)
lateinit var count: Int // ✗ 基本类型用 var + 延迟
lateinit var name: String // ✓ 对象类型
// 3. 必须手动初始化
lateinit var adapter: MyAdapter
// adapter.doSomething() // 错误:还没初始化
// 4. 判断是否已初始化
if (::adapter.isInitialized) {
adapter.doSomething()
}
lateinit 使用场景
kotlin复制代码
class MainActivity : AppCompatActivity() {
private lateinit var recyclerView: RecyclerView
private lateinit var adapter: FruitAdapter
private lateinit var toolbar: MaterialToolbar
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 初始化 ViewBinding
binding = ActivityMainBinding.inflate(layoutInflater)
// 初始化控件
recyclerView = findViewById(R.id.recyclerView)
adapter = FruitAdapter(dataList)
toolbar = findViewById(R.id.toolbar)
}
}
3. lateinit vs lazy
对比表
特性
lateinit
lazy
修饰符
var
val
首次访问
手动初始化
自动初始化
线程安全
否
是(默认)
基本类型
不可用
可用(by lazy { 0 })
内存
保留声明时的内存
只在首次访问时分配
lazy 延迟属性
kotlin复制代码
class User {
// val 不可变,用 lazy 延迟初始化
val database: Database by lazy {
println("初始化数据库...")
Database()
}
val config: Map<String, String> by lazy {
loadConfig()
}
}
// 第一次访问时才初始化
val user = User()
println(user.database) // 输出: 初始化数据库...,然后返回 Database 实例
println(user.database) // 直接返回已有的实例,不再打印
lazy 线程安全
kotlin复制代码
// 默认线程安全
val db1 by lazy { Database() }
// 非线程安全
val db2 by lazy(LazyThreadSafetyMode.NONE) { Database() }
// PUBLICATION:多个线程可能初始化多次(最终取其中一个)
val db3 by lazy(LazyThreadSafetyMode.PUBLICATION) { Database() }
场景对比
kotlin复制代码
// 场景一:Activity/Fragment 中的 View(用 lateinit)
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater) // 必须手动
}
}
// 场景二:单例或配置(用 lazy)
class App : Application() {
val database by lazy { Database.getInstance() }
val config by lazy { Config.load() }
}
// 场景三:RecyclerView Adapter(用 lateinit)
class MainActivity : AppCompatActivity() {
private lateinit var adapter: FruitAdapter
override fun onCreate(...) {
adapter = FruitAdapter(dataList) // 手动
}
}
// 场景四:复杂对象的延迟加载(用 lazy)
class UserProfile {
// 头像图片较大,延迟加载
val avatarBitmap by lazy {
BitmapFactory.decodeFile(avatarPath)
}
// 用户相册可能很大,按需加载
val photoGallery by lazy {
loadPhotosFromDisk()
}
}
常见错误
kotlin复制代码
// 错误 1:val 用 lateinit
lateinit val name: String // ✗
// 错误 2:基本类型用 lateinit
lateinit var count: Int // ✗
// 错误 3:未初始化就使用
lateinit var adapter: MyAdapter
adapter.notifyDataSetChanged() // ✗ 可能崩溃
// 正确:初始化后才能使用
lateinit var adapter: MyAdapter
adapter = MyAdapter() // 先初始化
adapter.notifyDataSetChanged() // ✓
最佳实践
kotlin复制代码
class MyActivity : AppCompatActivity() {
// ViewBinding - 必须用 lateinit(var)
private lateinit var binding: ActivityMainBinding
// 只读配置 - 用 lazy
private val apiUrl by lazy { BuildConfig.API_URL }
private val maxCacheSize by lazy { 100 * 1024 * 1024 }
// 数据适配器 - 用 lateinit(可能需要重新创建)
private lateinit var adapter: FruitAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
adapter = FruitAdapter(emptyList())
recyclerView.adapter = adapter
}
}