Kotlin快速入门

这是一篇 Kotlin 快速入门笔记,适合有 JS / TS 基础的开发者阅读。文章通过大量代码示例,梳理 Kotlin 的变量、类型、集合、控制流、函数、Lambda、类和数据类等基础语法,帮助读者快速建立 Kotlin 的语法体系。

1. Kotlin 基础

1.1入口文件

fun main() {
    println("Hello, world!")
    // Hello, world!
}

1.2变量

写法 含义 类似 JS / TS
val 只读变量 const
var 可变变量 let
String? 可空变量 `string
lateinit var 延迟初始化变量 先声明后赋值
const val 编译期常量 真正常量
val xxx by lazy 懒加载变量 第一次用时初始化
fun main() {
    // 1. val:只读变量,类似 JS / TS 的 const
    val name = "Tom"
    println(name)

    // name = "Jack" // 报错:val 不能重新赋值


    // 2. var:可变变量,类似 JS / TS 的 let
    var age = 18
    age = 20
    println(age)


    // 3. String?:可空变量,类似 TS 的 string | null
    var title: String? = "Kotlin 学习笔记"
    title = null
    println(title)


    // 4. lateinit var:延迟初始化变量,先声明,后赋值
    val user = User()
    user.username = "Alice"
    println(user.username)


    // 5. const val:编译期常量
    println(API_URL)


    // 6. val xxx by lazy:懒加载变量,第一次使用时才初始化
    println(config)
    println(config)
}

// lateinit var 通常写在类里面
class User {
    lateinit var username: String
}

// const val 必须写在顶层、object 或 companion object 中
const val API_URL = "https://example.com"

// lazy 变量第一次被访问时,才会执行初始化代码
val config: String by lazy {
    println("初始化 config")
    "这是配置内容"
}

1.3 字符串模板

fun main() {
    val name = "Tom"
    val age = 18
    val price = 99

    // 变量插值
    println("name = $name")

    // 表达式插值
    println("next age = ${age + 1}")

    // 属性访问
    println("name length = ${name.length}")

    // 函数调用
    println("upper name = ${name.uppercase()}")

    // if 表达式
    println("type = ${if (age >= 18) "adult" else "child"}")

    // 输出 $ 符号
    println("price = \$$price")

    // 多行字符串
    val message = """
        name = $name
        age = $age
        price = ${'$'}$price
    """.trimIndent()

    println(message)
}

1.4基本数据类型

类型 含义 示例 说明
Byte 8 位整数 val a: Byte = 127 范围:-128 ~ 127
Short 16 位整数 val a: Short = 1000 范围比 Byte 大
Int 32 位整数 val a: Int = 100 默认整数类型
Long 64 位整数 val a: Long = 100L 数字后加 L
Float 32 位浮点数 val a: Float = 3.14F 数字后加 F 或 f
Double 64 位浮点数 val a: Double = 3.14 默认小数类型
Boolean 布尔值 val ok: Boolean = true 只有 true / false
Char 单个字符 val c: Char = 'A' 使用单引号
String 字符串 val s: String = "Hello" 使用双引号
Array<T> 数组 val arr = arrayOf(1, 2, 3) 固定长度,元素类型统一
UByte 无符号 8 位整数 val a: UByte = 255u 范围:0 ~ 255
UShort 无符号 16 位整数 val a: UShort = 65535u 无负数
UInt 无符号 32 位整数 val a: UInt = 100u 数字后加 u
ULong 无符号 64 位整数 val a: ULong = 100uL 数字后加 uL
fun main() {
    val age: Int = 18
    val money: Long = 100000L
    val price: Double = 19.99
    val height: Float = 175.5F
    val isLogin: Boolean = true
    val grade: Char = 'A'
    val name: String = "Tom"
    val numbers: Array<Int> = arrayOf(1, 2, 3)

    println(age)
    println(money)
    println(price)
    println(height)
    println(isLogin)
    println(grade)
    println(name)
    println(numbers.contentToString())
}

1.5集合

类型 含义 是否有序 是否允许重复 是否可修改 示例
List<T> 只读列表 是 是 否 listOf(1, 2, 3)
MutableList<T> 可变列表 是 是 是 mutableListOf(1, 2, 3)
Set<T> 只读集合 通常不关心顺序 否 否 setOf(1, 2, 3)
MutableSet<T> 可变集合 通常不关心顺序 否 是 mutableSetOf(1, 2, 3)
Map<K, V> 只读键值对 按实现决定 Key 不允许重复 否 mapOf("a" to 1)
MutableMap<K, V> 可变键值对 按实现决定 Key 不允许重复 是 mutableMapOf("a" to 1)

常用操作函数:

函数 作用 示例
size 获取数量 list.size
isEmpty() 是否为空 list.isEmpty()
contains() 是否包含元素 list.contains("A")
first() 第一个元素 list.first()
last() 最后一个元素 list.last()
get(index) 根据索引获取 list.get(0)
[index] 根据索引获取 list[0]
filter {} 过滤 list.filter { it > 10 }
map {} 映射转换 list.map { it * 2 }
forEach {} 遍历 list.forEach { println(it) }

1.6控制流

1.6.1 if && when

语法 作用 是否能返回值 适合场景 示例形式
if 条件判断 是 简单二选一判断 if (age >= 18) "adult" else "child"
if else if 多条件判断 是 条件数量较少 if (...) ... else if (...) ... else ...
when (value) 按值匹配 是 类似 switch,适合多个固定值 when (day) { 1 -> ... }
when 多值匹配 多个值走同一分支 是 多个 case 结果相同 1, 2, 3 -> ...
when 范围匹配 判断值是否在范围内 是 分数、年龄、区间判断 in 60..100 -> ...
when 类型匹配 判断变量类型 是 Any 类型、父类类型判断 is String -> ...
when {} 不带参数的条件判断 是 替代复杂 if else if when { score >= 90 -> ... }
fun main() {
    val age = 18
    val score = 85
    val day = 6
    val value: Any = "Kotlin"

    // 1. if:简单二选一,替代三元表达式
    val userType = if (age >= 18) "adult" else "child"

    // 2. if else if:多个条件判断
    val ageLevel = if (age < 18) {
        "未成年"
    } else if (age < 60) {
        "成年人"
    } else {
        "老年人"
    }

    // 3. when(value):按固定值匹配
    val dayName = when (day) {
        1 -> "Monday"
        2 -> "Tuesday"
        3 -> "Wednesday"
        4 -> "Thursday"
        5 -> "Friday"
        6, 7 -> "Weekend" // 多个值匹配同一个分支
        else -> "Unknown"
    }

    // 4. when:范围匹配
    val scoreLevel = when (score) {
        in 90..100 -> "A"
        in 80..89 -> "B"
        in 60..79 -> "C"
        in 0..59 -> "D"
        else -> "Invalid"
    }

    // 5. when:类型匹配
    val typeResult = when (value) {
        is String -> "String length = ${value.length}"
        is Int -> "Int value = $value"
        is Boolean -> "Boolean value = $value"
        else -> "Unknown type"
    }

    // 6. when {}:不带参数,类似 if else if
    val comment = when {
        score >= 90 -> "优秀"
        score >= 80 -> "良好"
        score >= 60 -> "及格"
        else -> "不及格"
    }

    println(userType)
    println(ageLevel)
    println(dayName)
    println(scoreLevel)
    println(typeResult)
    println(comment)
}

1.6.2Ranges

写法 含义 是否包含结束值 示例 结果 / 含义
1..5 创建闭区间 包含 1..5 1, 2, 3, 4, 5
1..<5 创建半开区间 不包含 1..<5 1, 2, 3, 4
1 until 5 创建半开区间 不包含 1 until 5 1, 2, 3, 4
5 downTo 1 倒序区间 包含 5 downTo 1 5, 4, 3, 2, 1
1..10 step 2 指定步长 包含 1..10 step 2 1, 3, 5, 7, 9
5 downTo 1 step 2 倒序指定步长 包含 5 downTo 1 step 2 5, 3, 1
in 判断是否在范围内 - 3 in 1..5 true
!in 判断是否不在范围内 - 6 !in 1..5 true
'a'..'z' 字符范围 包含 'c' in 'a'..'z' true
array.indices 数组 / 集合索引范围 包含 list.indices 0..最后一个索引
fun main() {
    // 1. 闭区间:包含结束值
    for (i in 1..5) {
        print("$i ")
    }
    println()
    // 输出:1 2 3 4 5


    // 2. 半开区间:不包含结束值
    for (i in 1..<5) {
        print("$i ")
    }
    println()
    // 输出:1 2 3 4


    // 3. until:也是半开区间,不包含结束值
    for (i in 1 until 5) {
        print("$i ")
    }
    println()
    // 输出:1 2 3 4


    // 4. downTo:倒序
    for (i in 5 downTo 1) {
        print("$i ")
    }
    println()
    // 输出:5 4 3 2 1


    // 5. step:指定步长
    for (i in 1..10 step 2) {
        print("$i ")
    }
    println()
    // 输出:1 3 5 7 9


    // 6. downTo + step:倒序指定步长
    for (i in 10 downTo 1 step 3) {
        print("$i ")
    }
    println()
    // 输出:10 7 4 1


    // 7. in:判断是否在范围内
    val age = 18

    if (age in 18..60) {
        println("成年人")
    }


    // 8. !in:判断是否不在范围内
    val score = 120

    if (score !in 0..100) {
        println("分数非法")
    }


    // 9. Char 范围
    val char = 'k'

    if (char in 'a'..'z') {
        println("小写字母")
    }


    // 10. indices:遍历集合索引
    val names = listOf("Tom", "Alice", "Jack")

    for (index in names.indices) {
        println("index = $index, value = ${names[index]}")
    }
}

1.6.3Loops

语法 含义 是否先判断条件 适合场景 示例
for (item in collection) 遍历集合元素 是 遍历 List、Set、Array for (name in names)
for (i in range) 遍历范围 是 固定次数循环 for (i in 1..5)
for (i in collection.indices) 遍历索引 是 需要索引访问元素 for (i in list.indices)
for ((index, value) in list.withIndex()) 同时拿索引和值 是 需要索引和值 for ((i, v) in list.withIndex())
while (condition) 条件循环 是 不确定循环次数 while (count < 5)
do { } while (condition) 先执行一次再判断 否 至少执行一次 do { ... } while (...)
break 退出循环 - 满足条件时停止循环 break
continue 跳过本次循环 - 跳过当前项,继续下一次 continue
fun main() {
    // 1. for:遍历集合元素
    val names = listOf("Tom", "Alice", "Jack")

    for (name in names) {
        println(name)
    }


    // 2. for:遍历范围
    for (i in 1..5) {
        println("i = $i")
    }


    // 3. for:半开区间,不包含结束值
    for (i in 0 until 5) {
        println("index = $i")
    }


    // 4. for:倒序遍历
    for (i in 5 downTo 1) {
        println("倒序 i = $i")
    }


    // 5. for:指定步长
    for (i in 1..10 step 2) {
        println("step i = $i")
    }


    // 6. for:遍历集合索引
    for (index in names.indices) {
        println("index = $index, value = ${names[index]}")
    }


    // 7. for:同时获取索引和值
    for ((index, value) in names.withIndex()) {
        println("index = $index, value = $value")
    }


    // 8. while:先判断条件,再执行
    var count = 0

    while (count < 3) {
        println("while count = $count")
        count++
    }


    // 9. do while:先执行一次,再判断条件
    var num = 0

    do {
        println("do while num = $num")
        num++
    } while (num < 3)


    // 10. break:退出循环
    for (i in 1..10) {
        if (i == 5) {
            break
        }

        println("break 示例 i = $i")
    }


    // 11. continue:跳过本次循环
    for (i in 1..5) {
        if (i == 3) {
            continue
        }

        println("continue 示例 i = $i")
    }
}

1.7函数

类型 写法 含义 适合场景
普通函数 fun name() {} 定义一个函数 复用一段逻辑
带参数函数 fun add(a: Int, b: Int) 函数接收参数 需要外部传值
带返回值函数 fun add(...): Int 指定返回类型 计算并返回结果
Unit 返回值 fun log(): Unit 没有实际返回值 类似 JS 的 void
单表达式函数 fun add(a: Int, b: Int) = a + b 简化函数写法 函数体只有一行表达式
默认参数 fun hello(name: String = "Tom") 参数有默认值 调用时可省略参数
命名参数 hello(name = "Alice") 调用时指定参数名 提高可读性
可变参数 vararg nums: Int 接收多个同类型参数 参数数量不固定
Lambda { x: Int -> x * 2 } 匿名函数表达式 临时传递逻辑
函数类型 (Int) -> Int 表示函数的类型 函数作为变量 / 参数
高阶函数 fun calc(action: () -> Unit) 参数或返回值是函数 回调、集合操作
trailing lambda repeat(3) { ... } 最后一个参数是 Lambda 时可放到括号外 Kotlin 常见写法
it 简写 { it * 2 } 单参数 Lambda 的默认参数名 简化单参数 Lambda
fun main() {
    // 1. 普通函数
    sayHello()

    // 2. 带参数函数
    greet("Tom")

    // 3. 带返回值函数
    val sum = add(10, 20)
    println(sum)

    // 4. 单表达式函数
    println(multiply(3, 4))

    // 5. 默认参数
    hello()
    hello("Alice")

    // 6. 命名参数
    createUser(name = "Jack", age = 18)

    // 7. 可变参数 vararg
    println(sumAll(1, 2, 3, 4, 5))

    // 8. Lambda:赋值给变量
    val double: (Int) -> Int = { x: Int -> x * 2 }
    println(double(10))

    // 9. Lambda:单参数时可以用 it
    val triple: (Int) -> Int = { it * 3 }
    println(triple(10))

    // 10. 高阶函数:函数作为参数
    runTask {
        println("执行任务")
    }

    // 11. 自定义高阶函数
    val result = calculate(10, 20) { a, b ->
        a + b
    }
    println(result)

    // 12. 集合中常见 Lambda
    val numbers = listOf(1, 2, 3, 4, 5)

    val evenNumbers = numbers.filter { it % 2 == 0 }
    val doubleNumbers = numbers.map { it * 2 }

    println(evenNumbers)
    println(doubleNumbers)
}

// 普通函数
fun sayHello() {
    println("Hello Kotlin")
}

// 带参数函数
fun greet(name: String) {
    println("Hello, $name")
}

// 带返回值函数
fun add(a: Int, b: Int): Int {
    return a + b
}

// 单表达式函数
fun multiply(a: Int, b: Int): Int = a * b

// 返回 Unit,Unit 可以省略
fun log(message: String): Unit {
    println(message)
}

// 默认参数
fun hello(name: String = "Tom") {
    println("Hello, $name")
}

// 命名参数示例
fun createUser(name: String, age: Int) {
    println("name = $name, age = $age")
}

// 可变参数
fun sumAll(vararg numbers: Int): Int {
    var result = 0

    for (number in numbers) {
        result += number
    }

    return result
}

// 高阶函数:参数是一个函数
fun runTask(task: () -> Unit) {
    println("任务开始")
    task()
    println("任务结束")
}

// 高阶函数:接收两个 Int 和一个 Lambda
fun calculate(a: Int, b: Int, operation: (Int, Int) -> Int): Int {
    return operation(a, b)
}

1.7.1扩展函数

内容 写法 含义 适合场景
扩展函数 fun String.xxx() 给已有类型“增加函数” 不改源码的情况下扩展能力
接收者类型 String / Int / User 被扩展的类型 决定这个函数能被谁调用
接收者对象 this 当前调用扩展函数的对象 在函数内部访问调用者
可空类型扩展 fun String?.xxx() 给可空类型扩展函数 统一处理 null
泛型扩展函数 fun <T> List<T>.xxx() 给泛型类型扩展函数 扩展集合类能力
扩展属性 val String.xxx: Int 给已有类型增加“属性” 只读计算属性
作用范围 当前文件 / import 后可用 不是全局自动生效 需要在使用处能访问到
本质 静态函数 不是修改原类 编译后类似工具函数
fun main() {
    // 1. 给 String 扩展函数
    val text = "hello"

    println(text.addPrefix())
    println(text.firstChar())


    // 2. this 表示当前调用者
    val name = "Tom"

    println(name.sayHello())


    // 3. 给可空类型扩展函数
    val title1: String? = "Kotlin"
    val title2: String? = null

    println(title1.orDefault())
    println(title2.orDefault())


    // 4. 给自定义类型扩展函数
    val user = User("Alice", 20)

    println(user.displayInfo())


    // 5. 给集合扩展函数
    val numbers = listOf(1, 2, 3, 4, 5)

    println(numbers.secondOrNull())
    println(numbers.joinWithDash())


    // 6. 扩展属性
    println(text.lastIndex)
}


// 1. String 扩展函数
fun String.addPrefix(): String {
    return "Text: $this"
}


// 单表达式写法
fun String.firstChar(): Char {
    return this[0]
}


// 2. this 表示当前调用扩展函数的对象
fun String.sayHello(): String {
    return "Hello, $this"
}


// 3. 可空类型扩展函数
fun String?.orDefault(): String {
    return this ?: "默认值"
}


// 4. 自定义类型
data class User(
    val name: String,
    val age: Int
)

fun User.displayInfo(): String {
    return "name = $name, age = $age"
}


// 5. 泛型集合扩展函数
fun <T> List<T>.secondOrNull(): T? {
    return if (this.size >= 2) this[1] else null
}


fun List<Int>.joinWithDash(): String {
    return this.joinToString("-")
}


// 6. 扩展属性
val String.lastIndex: Int
    get() = this.length - 1

1.7.2带接收器的lambda表达式

写法 含义 类似理解
(T) -> R 普通 lambda,T 是参数 JS 里的 (obj) => result
T.() -> R 带接收者的 lambda,T 是 this 在 T 的上下文里执行代码
Canvas.() -> Unit lambda 内部的 this 是 Canvas 可以直接调用 Canvas 的方法
MutableList<Int>.() -> Unit lambda 内部的 this 是 MutableList<Int> 可以直接 add(...)
StringBuilder.() -> Unit lambda 内部的 this 是 StringBuilder 可以直接 append(...)
T.(A) -> R 既有接收者 T,又有普通参数 A this 是 T,同时还能接收参数
this.xxx 显式访问接收者对象 this.drawCircle()
xxx 省略 this 后的写法 drawCircle()
class Canvas {
    fun drawCircle() {
        println("Drawing circle")
    }

    fun drawSquare() {
        println("Drawing square")
    }
}

// block 的类型是 Canvas.() -> Unit
// 表示这个 lambda 执行时,内部的 this 是 Canvas
fun render(block: Canvas.() -> Unit) {
    val canvas = Canvas()

    // 等价于 block(canvas)
    // 但因为 block 是带接收者的 lambda,所以写成 canvas.block()
    canvas.block()
}

fun main() {
    render {
        // 这里的 this 是 Canvas

        drawCircle()
        drawSquare()

        // 等价于:
        // this.drawCircle()
        // this.drawSquare()
    }
}

1.8类

类型 写法 含义 适合场景
普通类 class User 定义一个类 需要封装属性和行为
主构造函数 class User(val name: String) 类名后面的构造函数 创建对象时传入参数
成员属性 val name: String / var age: Int 对象上的属性 保存对象状态
成员方法 fun sayHello() 类里面的函数 定义对象行为
init 块 init {} 初始化代码块 构造对象时执行额外逻辑
普通构造参数 class User(name: String) 只是参数,不是属性 只在初始化阶段使用
只读属性参数 class User(val name: String) 构造参数 + 只读属性 创建后可访问,不可修改
可变属性参数 class User(var name: String) 构造参数 + 可变属性 创建后可访问,可修改
数据类 data class User(...) 专门表示数据的类 DTO、接口返回值、状态对象
copy() user.copy(age = 20) 复制对象并修改部分字段 创建新对象
解构声明 val (name, age) = user 拆出数据类属性 快速取值
自动生成方法 toString() / equals() / hashCode() 数据类自动生成 比较、打印、集合操作
fun main() {
    // 1. 普通类:需要自己定义属性和方法
    val user = User("Tom", 18)

    println(user.name)
    println(user.age)

    user.age = 20
    println(user.age)

    user.sayHello()


    // 2. 构造参数不加 val / var:只是参数,不是对象属性
    val product = Product("Phone", 3999.0)

    println(product.displayName)
    println(product.isExpensive)

    // println(product.name)  // 报错:name 不是属性
    // println(product.price) // 报错:price 不是属性


    // 3. 数据类:主要用来存数据
    val person1 = Person("Alice", 20)
    val person2 = Person("Alice", 20)

    // data class 自动生成 toString()
    println(person1)

    // data class 自动生成 equals()
    println(person1 == person2) // true


    // 4. copy:复制对象,并修改部分字段
    val person3 = person1.copy(age = 21)

    println(person3)


    // 5. 解构声明
    val (name, age) = person1

    println(name)
    println(age)
}

// 普通类
class User(
    val name: String, // 只读属性
    var age: Int     // 可变属性
) {
    init {
        println("User 被创建了:$name, $age")
    }

    fun sayHello() {
        println("Hello, my name is $name")
    }
}

// 构造参数没有 val / var,只能在初始化阶段使用
class Product(name: String, price: Double) {
    val displayName = "商品名称:$name"
    val isExpensive = price > 1000

    init {
        println("创建商品:$name, 价格:$price")
    }
}

// 数据类
data class Person(
    val name: String,
    val age: Int
)

1.8.1类继承 && 抽象类 && 接口

概念 写法 含义 关键点
普通类 class User 定义一个类 默认不能被继承
可继承类 open class Animal 允许其他类继承 Kotlin 类默认是 final
继承类 class Dog : Animal() Dog 继承 Animal 父类有构造函数时要调用
抽象类 abstract class Product 不能直接创建对象,只能被继承 可以有普通方法,也可以有抽象方法
抽象属性 abstract val category: String 子类必须实现的属性 子类用 override 实现
抽象方法 abstract fun work() 子类必须实现的方法 没有方法体
接口 interface Flyable 定义一种能力或规范 不能有构造函数
实现接口 class Bird : Flyable 类实现接口能力 接口后面没有 ()
多接口实现 class Bird : Flyable, Runnable 一个类可以实现多个接口 Kotlin 类只能单继承,但接口可以多个
重写成员 override fun play() 实现或覆盖父级成员 Kotlin 必须显式写 override
接口委托 class A(b: B) : I by b 把接口实现交给另一个对象 可以减少手动转发代码
顶层父类 Any Kotlin 所有类最终都继承自它 类似 Java Object
// 抽象类:表示“是什么”
abstract class Animal(
    val name: String
) {
    // 抽象属性:子类必须实现
    abstract val type: String

    // 抽象方法:子类必须实现
    abstract fun makeSound()

    // 普通方法:子类可以直接复用
    fun info() {
        println("$name is a $type")
    }
}

// 接口:表示“能做什么”
interface Runnable {
    fun run()
}

interface Flyable {
    fun fly()
}

// Dog 继承 Animal,并实现 Runnable 接口
class Dog(name: String) : Animal(name), Runnable {
    override val type: String = "Dog"

    override fun makeSound() {
        println("$name says: Woof!")
    }

    override fun run() {
        println("$name is running")
    }
}

// Bird 继承 Animal,并实现 Runnable 和 Flyable 两个接口
class Bird(name: String) : Animal(name), Runnable, Flyable {
    override val type: String = "Bird"

    override fun makeSound() {
        println("$name says: Tweet!")
    }

    override fun run() {
        println("$name is running")
    }

    override fun fly() {
        println("$name is flying")
    }
}

fun main() {
    val dog = Dog("Buddy")
    dog.info()
    dog.makeSound()
    dog.run()

    val bird = Bird("Sky")
    bird.info()
    bird.makeSound()
    bird.run()
    bird.fly()
}

1.8.2 Objects

类型 写法 含义 常见用途 类似 JS / TS
对象声明 object AppConfig { ... } 定义一个单例对象 全局配置、工具对象、共享状态 单例对象 / module object
伴生对象 companion object { ... } 绑定在类上的单例对象 工厂方法、常量、模拟静态方法 static 方法/属性
对象表达式 object : Interface { ... } 创建匿名对象 临时实现接口、回调对象 匿名对象 / inline object
数据对象 data object Loading 带更好 toString() / 相等语义的单例 sealed 状态、固定状态值 enum-like singleton
对象继承接口 object X : SomeInterface 单例对象实现接口 默认实现、全局处理器 singleton implements interface
// 1. 单例对象:全局配置
object AppConfig {
    const val APP_NAME = "KotlinDemo"

    fun printConfig() {
        println("App name: $APP_NAME")
    }
}

// 2. 类 + companion object:工厂方法
class User private constructor(
    val name: String
) {
    companion object {
        fun create(name: String): User {
            return User(name)
        }
    }
}

// 3. 接口
interface Logger {
    fun log(message: String)
}

// 4. object expression:临时实现接口
fun runTask(logger: Logger) {
    logger.log("Task started")
}

// 5. data object:固定状态
sealed interface TaskState

data object Idle : TaskState
data object Loading : TaskState
data class Finished(val result: String) : TaskState

fun main() {
    AppConfig.printConfig()

    val user = User.create("Tom")
    println(user.name)

    runTask(object : Logger {
        override fun log(message: String) {
            println("[LOG] $message")
        }
    })

    val state: TaskState = Finished("OK")

    when (state) {
        Idle -> println("Idle")
        Loading -> println("Loading")
        is Finished -> println("Finished: ${state.result}")
    }
}

1.9作用域函数

函数 对象引用 返回值 主要用途 常见场景
let it Lambda 最后一行结果 转换结果、处理可空对象 xxx?.let { ... }
run this Lambda 最后一行结果 在对象作用域内计算结果 初始化后返回计算值
with this Lambda 最后一行结果 对已有对象连续操作 不需要链式调用时
apply this 原对象本身 配置对象属性 创建对象后初始化属性
also it 原对象本身 附加操作、副作用 打日志、调试、额外处理
fun main() {
    val user = User("Tom", 18)

    // 1. let:常用于可空对象处理,返回 Lambda 最后一行结果
    val nameLength = user.name.let {
        println("name = $it")
        it.length
    }

    println(nameLength)


    // 2. run:this 指向当前对象,返回 Lambda 最后一行结果
    val description = user.run {
        "name = $name, age = $age"
    }

    println(description)


    // 3. with:不是扩展函数,适合对已有对象做多次操作
    val info = with(user) {
        println("访问 name = $name")
        println("访问 age = $age")

        "User($name, $age)"
    }

    println(info)


    // 4. apply:返回原对象,常用于对象初始化
    val newUser = User("Alice", 20).apply {
        age = 21
        city = "Shanghai"
    }

    println(newUser)


    // 5. also:返回原对象,常用于日志、调试、额外操作
    val resultUser = User("Jack", 30).also {
        println("创建了用户:$it")
    }

    println(resultUser)


    // 6. 可空对象常见写法
    val nullableName: String? = "Kotlin"

    nullableName?.let {
        println("字符串不为空,长度 = ${it.length}")
    }


    // 7. 链式调用示例
    val finalUser = User("Bob", 25)
        .also {
            println("初始化前:$it")
        }
        .apply {
            age = 26
            city = "Beijing"
        }
        .also {
            println("初始化后:$it")
        }

    println(finalUser)
}

data class User(
    var name: String,
    var age: Int,
    var city: String = ""
)