Kotlin 高级特性
密封类(Sealed Class)
概念和特点
-
定义
- 限制类继承结构的类
- 所有子类必须在同一文件中声明
- 编译时类型安全
-
使用场景
- 表示受限的类层次结构
- 状态管理
- 错误处理
示例代码
sealed class Result<out T> {
data class Success<T>(val data: T) : Result<T>()
data class Error(val exception: Exception) : Result<Nothing>()
object Loading : Result<Nothing>()
}
fun handleResult(result: Result<String>) = when (result) {
is Result.Success -> println(result.data)
is Result.Error -> println(result.exception.message)
is Result.Loading -> println("Loading...")
// 不需要else分支,编译器能确保所有情况都被处理
}委托属性
核心概念
-
属性委托
- 将属性的getter/setter委托给另一个类
- 复用属性访问逻辑
- 支持懒加载
-
内置委托
- lazy
- observable
- vetoable
- notNull
实现示例
// 自定义委托
class PreferenceDelegate<T>(
private val context: Context,
private val name: String,
private val default: T
) {
private val prefs by lazy {
context.getSharedPreferences("default", Context.MODE_PRIVATE)
}
operator fun getValue(thisRef: Any?, property: KProperty<*>): T {
return findPreference(name, default)
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
savePreference(name, value)
}
}
// 使用示例
class SettingsManager(context: Context) {
var username by PreferenceDelegate(context, "username", "")
var isFirstLaunch by PreferenceDelegate(context, "isFirstLaunch", true)
}内联类(Inline Class)
基本概念
-
特点
- 在运行时表示为包装的类型
- 编译时会被优化,减少内存分配
- 只能有一个主构造器参数
-
使用场景
- 度量单位转换
- 类型安全的标识符
- 性能优化
代码示例
@JvmInline
value class Password(private val value: String)
@JvmInline
value class Meters(val value: Double) {
fun toKilometers(): Kilometers = Kilometers(value / 1000)
}
@JvmInline
value class Kilometers(val value: Double)
// 使用示例
fun login(username: String, password: Password) {
// 类型安全的密码处理
}
val distance = Meters(5000.0).toKilometers()扩展函数(Extension Functions)
核心特性
-
定义方式
- 不修改原类的情况下添加新功能
- 可以访问接收者的public成员
- 支持泛型扩展
-
使用限制
- 不能访问私有成员
- 不能重写已有方法
- 扩展属性不能有backing field
实践示例
// 字符串扩展
fun String.removeFirstAndLast() = substring(1, length - 1)
// 泛型扩展
fun <T> List<T>.swap(index1: Int, index2: Int): List<T> {
val result = this.toMutableList()
val tmp = result[index1]
result[index1] = result[index2]
result[index2] = tmp
return result
}
// 扩展属性
val <T> List<T>.lastIndex: Int
get() = size - 1协程(Coroutines)
基础概念
-
特点
- 轻量级线程
- 支持异步编程
- 结构化并发
-
核心组件
- CoroutineScope
- CoroutineContext
- Job
- Dispatcher
实现示例
// 基本使用
fun main() = runBlocking {
launch {
delay(1000)
println("World!")
}
println("Hello")
}
// 异步操作
suspend fun fetchUser(): User = coroutineScope {
val user = async { api.getUser() }
val friends = async { api.getFriends() }
user.await().copy(friends = friends.await())
}
// 错误处理
val handler = CoroutineExceptionHandler { _, exception ->
println("Caught $exception")
}
val job = GlobalScope.launch(handler) {
throw AssertionError("My Custom Error")
}面试题精选
1. 密封类和枚举类的区别是什么?
答案:
- 密封类可以包含状态,而枚举常量不能
- 密封类的子类可以有多个实例,枚举常量只能有一个实例
- 密封类可以继承其他类,枚举类不能
- 密封类更适合表示复杂的状态层次结构
2. 什么是属性委托?有哪些常用的委托模式?
答案:
- 属性委托是将属性的getter/setter委托给另一个类处理的机制
- 常用的委托模式:
- lazy:懒加载
- observable:观察属性变化
- vetoable:可否决的属性修改
- notNull:非空属性
- map:将属性存储在Map中
3. 内联类的主要用途是什么?
答案:
- 性能优化:避免运行时装箱操作
- 类型安全:提供特定类型的包装
- 零成本抽象:编译时会被优化掉
- 适用场景:
- 度量单位转换
- 类型安全的标识符
- 性能敏感的包装类
4. 协程与线程的区别是什么?
答案:
- 资源消耗:协程更轻量,可以创建大量实例
- 切换开销:协程切换成本低,不涉及线程切换
- 编程模型:协程支持结构化并发,代码更清晰
- 异常处理:协程提供内置的异常处理机制
- 取消机制:协程支持结构化取消
5. Kotlin的扩展函数有什么限制?
答案:
- 不能访问接收者类的私有成员
- 不能重写已有的成员方法
- 扩展属性不能有backing field
- 扩展函数的解析是静态的
- 子类扩展函数不能覆盖父类扩展函数