1. Kotlin 简介及特性
Kotlin 是一门由 JetBrains 开发的现代编程语言,可以在 JVM 上运行。主要特性包括:
- 空安全(Null Safety)
- 简洁的语法
- 与 Java 100% 互操作
- 支持函数式编程
- 智能类型推断
- 协程支持
2. @JvmOverloads 注解
@JvmOverloads
的作用是在有默认参数值的函数中自动生成重载函数。
kotlin 代码解读复制代码class Example {
@JvmOverloads
fun greet(name: String = "World", greeting: String = "Hello") {
println("$greeting, $name!")
}
}
这会生成以下 Java 代码:
java 代码解读复制代码class Example {
public void greet(String name, String greeting) { ... }
public void greet(String name) { ... }
public void greet() { ... }
}
3. List 与 MutableList 区别
List
: 不可变集合,只能读取MutableList
: 可变集合,可以进行增删改操作
kotlin 代码解读复制代码val readOnlyList: List = listOf("a", "b", "c")
val mutableList: MutableList = mutableListOf("a", "b", "c")
// 只能读取
readOnlyList[0] // 正确
// readOnlyList.add("d") // 错误!
// 可以修改
mutableList.add("d") // 正确
4. Kotlin 实现单例的方式
- object 关键字(最简单):
kotlin 代码解读复制代码object Singleton {
fun doSomething() {}
}
- 伴生对象:
kotlin 代码解读复制代码class Singleton private constructor() {
companion object {
@Volatile private var instance: Singleton? = null
fun getInstance(): Singleton =
instance ?: synchronized(this) {
instance ?: Singleton().also { instance = it }
}
}
}
5. data 关键字
data 类自动生成:
- equals()/hashCode()
- toString()
- componentN()
- copy()
kotlin 代码解读复制代码data class User(val name: String, val age: Int)
6. 委托属性
委托属性允许将属性的 getter/setter 委托给另一个类。
常见使用场景:
- 懒加载(lazy)
- 观察者模式(Observable)
- 存储属性(如 SharedPreferences)
kotlin 代码解读复制代码class Example {
private val heavyObject by lazy {
// 复杂初始化
HeavyObject()
}
}
7. with 与 apply 区别
kotlin 代码解读复制代码// with 返回最后一行结果
val result = with(person) {
println(name)
age + 1
}
// apply 返回对象本身
val person = Person().apply {
name = "张三"
age = 20
}
8. 协程与线程的区别
协程比线程轻量的原因:
- 内存占用少:协程只需要几十字节的内存
- 上下文切换成本低:协程是用户态的切换
- 可以在单线程上运行多个协程
简单的流程图:
9. infix 关键字
infix 允许以中缀表达式的形式调用函数。
kotlin 代码解读复制代码class Person {
infix fun likes(thing: String) {
println("I like $thing")
}
}
// 使用方式:
val person = Person()
person likes "coding" // 等同于 person.likes("coding")
10. Kotlin 可见性修饰符
Kotlin 的可见性修饰符:
public
(默认):所有地方可见private
:当前类内可见protected
:当前类及子类可见internal
:同一模块内可见
与 Java 的主要区别:
- Kotlin 默认是 public
- Kotlin 新增 internal
- Kotlin 的外部类不能访问内部类的 private 成员
11. Kotlin 与 Java 混合开发注意事项
- 空安全处理:
kotlin 代码解读复制代码// Java 代码返回的类型在 Kotlin 中会被认为是可空的
fun handleJavaMethod(javaString: String?) {
javaString?.length ?: 0
}
- 静态成员访问:
kotlin 代码解读复制代码// Java 静态方法在 Kotlin 中的调用
JavaClass.Companion.staticMethod()
- SAM 转换注意事项:
kotlin 代码解读复制代码// Java 接口
interface OnClickListener {
void onClick(View view);
}
// Kotlin 调用
view.setOnClickListener { view ->
// 处理点击
}
12. Kotlin 解构
解构允许将一个对象的多个属性同时赋值给多个变量:
kotlin 代码解读复制代码data class Person(val name: String, val age: Int)
val person = Person("张三", 25)
val (name, age) = person
// 背后的原理是通过 componentN() 函数实现
val name = person.component1()
val age = person.component2()
Kotlin 的解构声明(Destructuring Declarations)底层是通过 componentN()
函数实现的。让我详细解释:
- 基本使用:
kotlin 代码解读复制代码// 数据类自动支持解构
data class User(val name: String, val age: Int)
fun example() {
val user = User("Tom", 20)
val (name, age) = user // 解构声明
// 底层实际调用
val name = user.component1()
val age = user.component2()
}
- 自定义解构:
kotlin 代码解读复制代码class Point(val x: Int, val y: Int) {
// 手动实现 componentN 函数
operator fun component1() = x
operator fun component2() = y
}
// 使用
val point = Point(10, 20)
val (x, y) = point
- 常见应用场景:
kotlin 代码解读复制代码// 1. Map 遍历
val map = mapOf("a" to 1, "b" to 2)
for ((key, value) in map) {
println("$key = $value")
}
// 2. 函数返回多个值
data class Result(val success: Boolean, val data: String)
fun getData(): Result {
return Result(true, "数据")
}
val (success, data) = getData()
// 3. Lambda 参数
map.forEach { (key, value) ->
println("$key = $value")
}
- 解构原理示例:
kotlin 代码解读复制代码// 编译前
data class Point(val x: Int, val y: Int)
val (a, b) = Point(1, 2)
// 编译后等价于
data class Point(val x: Int, val y: Int) {
operator fun component1() = x
operator fun component2() = y
}
val point = Point(1, 2)
val a = point.component1()
val b = point.component2()
- 实际应用:
kotlin 代码解读复制代码// 1. 处理网络响应
data class ApiResponse<T>(
val code: Int,
val message: String,
val data: T?
)
fun handleResponse() {
val response = getApiResponse()
val (code, message, data) = response
// 处理响应
}
// 2. 状态管理
data class UiState(
val isLoading: Boolean,
val data: List- ,
val error: String?
)
fun render(state: UiState) {
val (isLoading, items, error) = state
// 更新 UI
}
总结:
- 解构的实现方式:
- 通过
componentN()
函数 - 数据类自动生成
- 可手动实现
- 常用场景:
- Map 遍历
- 多返回值
- 状态管理
- 参数解构
- 注意事项:
- 解构顺序要对应
- 可以忽略部分值用
_
- 注意性能开销
- 最佳实践:
- 适度使用
- 保持代码清晰
- 考虑可读性
13. 内联函数
内联函数可以减少 Lambda 表达式的性能开销:
kotlin 代码解读复制代码inline fun measureTime(block: () -> Unit) {
val start = System.currentTimeMillis()
block()
println("耗时:${System.currentTimeMillis() - start}ms")
}
内联函数的工作原理图:
14. Kotlin 构造方法
Kotlin 有主构造函数和次构造函数:
kotlin 代码解读复制代码class Person(val name: String) { // 主构造函数
var age: Int = 0
constructor(name: String, age: Int) : this(name) { // 次构造函数
this.age = age
}
init {
// 初始化代码块
println("Creating person: $name")
}
}
15. Sequence
Sequence 是惰性求值的序列,对比即时求值的 List 更高效:
kotlin 代码解读复制代码// List 方式(即时求值)
listOf(1, 2, 3, 4)
.map { it * 2 } // 创建新列表 [2, 4, 6, 8]
.filter { it > 5 } // 创建新列表 [6, 8]
.first() // 返回 6
// Sequence 方式(惰性求值)
sequenceOf(1, 2, 3, 4)
.map { it * 2 } // 不立即执行
.filter { it > 5 } // 不立即执行
.first() // 只处理需要的元素
性能比较图:
16. 空安全处理
Kotlin 提供多种空安全处理机制:
kotlin 代码解读复制代码// 安全调用操作符
val length = str?.length
// Elvis 操作符
val length = str?.length ?: 0
// 非空断言
val length = str!!.length
// 智能转换
if (str != null) {
println(str.length) // 编译器知道 str 非空
}
17. Any 与 Object
主要区别:
- Any 是所有 Kotlin 类的超类
- Any 只有 equals()、hashCode() 和 toString() 三个方法
- Any 可以是空类型(Any?)
- Object 是所有 Java 类的超类
18. 集合遍历方式
kotlin 代码解读复制代码val list = listOf(1, 2, 3)
// 1. for 循环
for (item in list) { }
// 2. forEach
list.forEach { }
// 3. 索引遍历
for (i in list.indices) { }
// 4. withIndex
for ((index, value) in list.withIndex()) { }
19. Kotlin 数据类型隐式转换
Kotlin 不支持数据类型的隐式转换,这是为了避免精度损失和运行时错误。
kotlin 代码解读复制代码val intNumber: Int = 100
// val longNumber: Long = intNumber // 编译错误
val longNumber: Long = intNumber.toLong() // 正确方式
// 数字类型转换方法
val number = 100
number.toByte()
number.toShort()
number.toInt()
number.toLong()
number.toFloat()
number.toDouble()
转换关系图:
20. 对象表达式与 Lambda 表达式的区别
kotlin 代码解读复制代码// 1. 对象表达式
val clickListener = object : View.OnClickListener {
override fun onClick(v: View?) {
// 处理点击
}
}
// 2. Lambda 表达式
val clickListener = { v: View? ->
// 处理点击
}
主要区别:
-
内存分配:
- 对象表达式会创建匿名类实例
- Lambda 表达式通常会被编译器优化,不会创建额外对象
-
功能性:
- 对象表达式可以实现多个接口
- Lambda 表达式只能实现单个抽象方法
21. 协程的深入理解
协程的基本组件:
kotlin 代码解读复制代码// 1. 启动协程
GlobalScope.launch {
// 协程代码
}
// 2. 使用 suspend 函数
suspend fun fetchData(): Data {
delay(1000) // 非阻塞延迟
return Data()
}
// 3. 协程作用域
class MyViewModel : ViewModel() {
private val viewModelScope = CoroutineScope(Dispatchers.Main)
fun loadData() {
viewModelScope.launch {
val data = fetchData()
// 处理数据
}
}
}
协程的生命周期图:
22. 协程的上下文和调度器
kotlin 代码解读复制代码// 不同调度器的使用
coroutineScope {
// UI 线程
launch(Dispatchers.Main) {
updateUI()
}
// IO 操作
launch(Dispatchers.IO) {
fetchData()
}
// CPU 密集型操作
launch(Dispatchers.Default) {
processData()
}
}
调度器选择流程:
23. 协程的异常处理
kotlin 代码解读复制代码// 1. try-catch 方式
launch {
try {
// 可能抛出异常的代码
} catch (e: Exception) {
// 处理异常
}
}
// 2. 协程作用域的异常处理
val handler = CoroutineExceptionHandler { _, exception ->
println("Caught $exception")
}
GlobalScope.launch(handler) {
throw Exception("Error")
}
// 3. SupervisorJob 方式
val scope = CoroutineScope(SupervisorJob() + handler)
24. 协程与线程的详细对比
特性 | 协程 | 线程 |
---|---|---|
内存占用 | ~几十字节 | ~1MB |
切换成本 | 用户态切换,非常轻量 | 系统态切换,较重 |
并发量 | 可同时运行数十万个 | 受系统资源限制 |
调度控制 | 完全由程序控制 | 依赖操作系统调度 |
取消操作 | 支持结构化取消 | 难以安全取消 |
25. 协程的最佳实践
kotlin 代码解读复制代码class Repository {
// 1. 使用适当的作用域
private val coroutineScope = CoroutineScope(
Dispatchers.IO + SupervisorJob()
)
// 2. 结构化并发
suspend fun fetchData() = coroutineScope {
val part1 = async { fetchPart1() }
val part2 = async { fetchPart2() }
combineResults(part1.await(), part2.await())
}
// 3. 优雅的异常处理
private val handler = CoroutineExceptionHandler { _, e ->
// 处理异常
}
}
26.Unit 与 Void 的区别
-
类型系统:
Unit
是一个实际的类,只有一个单例实例Void
是一个不能实例化的类型
-
返回值:
kotlin 代码解读复制代码// Kotlin中的Unit
fun doSomething(): Unit {
// 不需要显式返回任何值
}
// Java中的void
public void doSomething() {
// 不能返回值
}
- 泛型使用:
kotlin 代码解读复制代码// Kotlin中可以使用Unit作为泛型参数
interface Callback<T> {
fun onSuccess(value: T)
fun onFailure(error: Throwable)
}
// Unit用作泛型参数
val callback = object : Callback<Unit> {
override fun onSuccess(value: Unit) {
// 处理成功
}
override fun onFailure(error: Throwable) {
// 处理失败
}
}
- 函数类型:
kotlin 代码解读复制代码// Unit在函数类型中的使用
val action: () -> Unit = { println("Hello") }
// 等价的Java代码需要使用Void
Runnable runnable = () -> System.out.println("Hello");
主要区别总结:
- Unit 是一个具体的类型,有一个单例实例
- Void 不能实例化
- Unit 可以用在泛型中
- Unit 在函数式编程中更加灵活
- Kotlin 中的 Unit 相当于其他函数式编程语言中的 unit 或 void 类型
评论记录:
回复评论: