一. Kotlin基础语法

2026-07-21 23:43 327 阅读

Kotlin 语法文档


基础篇


1. Kotlin 简介

Kotlin 是一种现代的静态类型编程语言,由 JetBrains 开发,运行在 JVM 上,也可编译为 JavaScript 或原生代码(Kotlin/Native)。它是 Google 官方推荐的 Android 开发语言。

核心优势

  • 简洁:大幅减少样板代码,相比 Java 代码量可减少约 40%
  • 安全:空安全设计从语言层面杜绝空指针异常
  • 互操作:与 Java 100% 互通,可直接使用现有 Java 库
  • 函数式:一等公民的函数支持,Lambda 表达式流畅自然
  • 协程:轻量级并发,告别回调地狱

运行环境

环境 说明
JVM Android / 后端服务
JavaScript 前端开发
Native iOS / 桌面 / 嵌入式

💡 提示:Kotlin 完全兼容 Java,可以在现有 Java 项目中逐步引入 Kotlin 代码,无需重写整个项目。


2. 基本语法

包声明与导入

// 包声明(必须在文件顶部)
package com.example.demo

// 导入单个类
import kotlin.math.PI

// 导入包下所有内容
import kotlin.math.*

// 别名导入(解决命名冲突)
import java.util.Date as JavaDate
import java.sql.Date as SqlDate

// 使用
val now = JavaDate()

Hello World

fun main() {
    println("Hello, Kotlin!")
}

Kotlin 文件以 .kt 为扩展名。main 函数是程序入口点。与 Java 不同,Kotlin 不需要将代码放在类中(脚本式写法),也不需要在每行末尾写分号。

变量声明

// 只读变量(类似 final,推荐优先使用)
val name: String = "Alice"
val age = 25          // 类型推断,编译器自动推导为 Int

// 可变变量
var count: Int = 0
count = 10            // var 允许重新赋值

// 常量(编译期确定)
const val MAX_SIZE = 100
关键字 含义 可变性
val 只读变量(read-only) 不可重新赋值,但对象内部状态可变
var 可变变量(mutable) 可重新赋值
const val 编译期常量 仅限顶层或伴生对象,值必须编译期确定

基本数据类型

类型 位宽 示例 说明
Byte 8 位 val b: Byte = 1 范围 -128 到 127
Short 16 位 val s: Short = 100 范围 -32768 到 32767
Int 32 位 val i = 100 最常用整数类型
Long 64 位 val l = 100L 大整数,后缀 L
Float 32 位 val f = 3.14f 单精度浮点,后缀 f
Double 64 位 val d = 3.14 双精度浮点(默认)
Boolean val b = true true / false
Char 16 位 val c = 'A' 单字符,用单引号
String val s = "Hi" 字符串,不可变

⚠️ 注意:Kotlin 中所有基本类型都是对象(没有 Java 中的 intInteger 之分),编译器会根据情况自动优化为原始类型。

字符串模板

val name = "Alice"
val age = 25

// 简单变量引用
val message = "Name: $name, Age: $age"

// 表达式(花括号包裹)
val info = "Next year: ${age + 1}"

// 原始字符串(三重引号,支持多行)
val json = """
    {
        "name": "$name",
        "age": $age
    }
"""

3. 条件控制

if 表达式

Kotlin 中 if表达式(有返回值),不是语句:

val max = if (a > b) a else b

// 带代码块的 if
val result = if (score >= 90) {
    println("优秀")
    "A"
} else if (score >= 80) {
    println("良好")
    "B"
} else {
    "C"
}

when 表达式(替代 switch)

when (x) {
    1 -> println("x == 1")
    2, 3 -> println("x is 2 or 3")         // 多值匹配
    in 4..10 -> println("x in range")       // 范围匹配
    is String -> println("x is String")     // 类型匹配
    else -> println("otherwise")             // 默认分支
}

when 也可以作为表达式返回值:

val description = when (x) {
    in 0..17 -> "未成年"
    in 18..64 -> "成年人"
    else -> "老年人"
}

4. 循环

// for 循环 - 区间
for (i in 1..5) {           // 包含 5(闭区间)
    println(i)               // 输出 1 2 3 4 5
}

for (i in 1 until 5) {      // 不包含 5(半开区间)
    println(i)               // 输出 1 2 3 4
}

for (i in 5 downTo 1) {     // 递减
    println(i)               // 输出 5 4 3 2 1
}

for (i in 1..10 step 2) {   // 步长为 2
    println(i)               // 输出 1 3 5 7 9
}

// for 循环 - 集合
val list = listOf("a", "b", "c")
for (item in list) {
    println(item)
}

for ((index, item) in list.withIndex()) {
    println("$index: $item")
}

// while 循环
var x = 0
while (x < 5) {
    println(x++)
}

// do-while(至少执行一次)
do {
    println("至少执行一次")
} while (false)

5. 函数定义

// 普通函数
fun sum(a: Int, b: Int): Int {
    return a + b
}

// 单表达式函数(自动推断返回类型)
fun multiply(a: Int, b: Int) = a * b

// 默认参数
fun greet(name: String = "World") {
    println("Hello, $name!")
}

// 命名参数调用
greet(name = "Alice")

// 可变参数
fun printAll(vararg items: String) {
    for (item in items) println(item)
}
printAll("a", "b", "c")

// 返回多个值(Pair / Triple / 数据类)
fun getMinMax(list: List<Int>): Pair<Int, Int> {
    return Pair(list.min(), list.max())
}
val (min, max) = getMinMax(listOf(1, 2, 3))

6. 空安全(Null Safety)

Kotlin 的类型系统从语言层面区分可空和非空类型,从根本上减少空指针异常(NPE)。

// 非空类型(默认)—— 不能赋值为 null
var name: String = "Alice"
// name = null  // 编译错误!

// 可空类型 —— 加 ? 后缀
var nullableName: String? = "Bob"
nullableName = null  // 允许

// 1. 安全调用 ?.
val length = nullableName?.length       // 返回 Int?(null 时返回 null)

// 2. Elvis 操作符 ?:(提供默认值)
val len = nullableName?.length ?: 0    // null 时返回 0

// 3. 非空断言 !!(慎用,可能抛 NPE)
val notNull = nullableName!!           // 确定非空时使用

// 4. 安全类型转换 as?
val str: String? = value as? String    // 转换失败返回 null

// 5. let 作用域函数
nullableName?.let {
    println("Name length: ${it.length}")  // 仅当非空时执行
}
操作符 名称 作用
?. 安全调用 对象为 null 时返回 null,不抛异常
?: Elvis 操作符 左侧为 null 时返回右侧默认值
!! 非空断言 强制认为非空,为 null 时抛 NPE
as? 安全转换 转换失败返回 null,不抛异常

💡 提示:优先使用 val + 非空类型,尽量避免使用 !!。如果某个值可能为空,用 ?.?: 优雅地处理。


7. 异常处理

Kotlin 中 try 是表达式,可以返回值。Kotlin 不区分受检异常和运行时异常——所有异常都是非受检的(无需在方法签名中声明 throws)。

// 基本 try-catch-finally
try {
    val num = "abc".toInt()
} catch (e: NumberFormatException) {
    println("格式错误: ${e.message}")
} finally {
    println("清理资源(无论如何都会执行)")
}

// try 是表达式(可以返回值)
val result: Int? = try {
    "123".toInt()
} catch (e: NumberFormatException) {
    null  // 解析失败返回 null
}

// 可以捕获多个异常类型
try {
    // ...
} catch (e: IOException) {
    println("IO 异常")
} catch (e: Exception) {
    println("其他异常")
}

// throw 抛异常
fun validate(age: Int) {
    if (age < 0) throw IllegalArgumentException("年龄不能为负数: $age")
}

💡 提示:如果与 Java 互操作,需要让 Java 看到受检异常时,使用 @Throws(IOException::class) 注解。


8. 集合操作

集合类型

类型 不可变 可变 特点
List listOf() mutableListOf() 有序、可重复
Set setOf() mutableSetOf() 无序、不重复
Map mapOf() mutableMapOf() 键值对
// 创建集合
val list = listOf(1, 2, 3, 4, 5)
val mutableList = mutableListOf(1, 2, 3)
mutableList.add(4)

val set = setOf("a", "b", "a")         // 实际只有 "a", "b"
val map = mapOf("key1" to "value1", "key2" to "value2")

// 常用操作
val filtered = list.filter { it > 2 }     // [3, 4, 5]
val mapped = list.map { it * 2 }          // [2, 4, 6, 8, 10]
val sum = list.sum()                      // 15
val average = list.average()              // 3.0
val grouped = list.groupBy { it % 2 }     // {1=[1,3,5], 0=[2,4]}

// 链式调用
list.filter { it % 2 == 0 }
    .map { it * it }
    .sortedDescending()
    .forEach { println(it) }

序列(Sequence)——惰性求值

List 的每一步操作都创建中间集合(急切求值);Sequence 只在终止操作时执行一次(惰性求值),数据量大时可避免创建大量中间集合。

// List 操作:每一步都创建中间集合(急切求值)
val result1 = list.filter { it > 2 }  // 创建新 List
    .map { it * 2 }                    // 再创建新 List
    .toList()

// Sequence:惰性求值,只在终止操作时执行一次
val result2 = list.asSequence()
    .filter { it > 2 }
    .map { it * 2 }
    .toList()  // 终止操作,才真正执行

// 无限序列
val naturals = generateSequence(0) { it + 1 }
val firstTen = naturals.take(10).toList()  // [0..9]

// yield 构建序列 *
val fibonacci = sequence {
    var a = 0
    var b = 1
    yield(a)
    yield(b)
    while (true) {
        val next = a + b
        yield(next)
        a = b
        b = next
    }
}
println(fibonacci.take(10).toList())
// [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

💡 提示:数据量大或操作链长时,优先使用 Sequence 避免创建大量中间集合。但元素很少时(<100),List 操作反而更快(无额外开销)。


9. 类与对象

类的基本结构

// 主构造函数(最常用写法)
class Person(
    val name: String,       // 自动生成属性 + getter
    var age: Int,            // 自动生成属性 + getter/setter
    private val id: String   // 私有属性
) {
    // 初始化代码块
    init {
        println("Person created: $name, $age")
    }

    // 成员函数
    fun introduce() {
        println("Hi, I'm $name, $age years old")
    }
}

// 使用
val person = Person("Alice", 25, "ID001")
person.introduce()

主构造 vs 次构造

class Person constructor(name: String) {  // constructor 关键字可省略
    val name: String

    // 初始化块
    init {
        this.name = name
    }

    // 次构造函数(必须委托给主构造)
    constructor(name: String, age: Int) : this(name) {
        println("Age: $age")
    }
}

// 更简洁的写法(推荐)
class Person(val name: String) {
    constructor(name: String, age: Int) : this(name) {}
}

10. 可见性修饰符

修饰符 可见范围
public(默认) 任何地方可见
internal 同一模块内可见
protected 当前类及子类可见
private 当前类 / 当前文件内可见(顶层声明时)
// 文件级私有(同一 .kt 文件内可见)
private val config = "secret"

// 类中的修饰符
open class Base {
    public val a = 1       // 任何地方可见
    internal val b = 2     // 同一模块可见
    protected val c = 3    // 子类可见
    private val d = 4      // 仅本类可见
}

class Derived : Base() {
    fun access() {
        println(c)  // 可以访问 protected
        // println(d)  // 编译错误:private 不可见
    }
}

11. 继承与抽象类

Kotlin 中类默认是 final 的,需要用 open 修饰才能被继承。

// 基类(open 修饰)
open class Animal(val name: String) {
    open fun makeSound() {
        println("$name makes a sound")
    }
}

// 子类
class Dog(name: String, val breed: String) : Animal(name) {
    override fun makeSound() {
        super.makeSound()
        println("$name barks: Woof!")
    }
}

// 抽象类
abstract class Vehicle {
    abstract fun start()
    abstract fun stop()

    // 抽象类可以有具体实现
    fun honk() = println("Beep beep!")
}

class Car : Vehicle() {
    override fun start() = println("Car started")
    override fun stop() = println("Car stopped")
}
修饰符 含义
open 允许类被继承 / 方法被重写
abstract 抽象类或抽象方法,必须被实现
final 禁止继承或重写(默认)
override 重写父类方法或属性
super 调用父类实现

12. 接口

// 接口可以包含抽象方法和带默认实现的方法
interface Flyable {
    fun fly()

    // 默认实现
    fun takeOff() {
        println("Taking off...")
        fly()
    }
}

interface Swimmable {
    fun swim()
}

// 接口中的属性
interface Named {
    val name: String            // 抽象属性
    val displayName: String     // 可以有 getter
        get() = "Named: $name"
}

// 一个类可以实现多个接口
class Duck(override val name: String) : Flyable, Swimmable, Named {
    override fun fly() = println("$name is flying")
    override fun swim() = println("$name is swimming")
}

13. 多态与智能类型转换

open class Shape {
    open fun draw() = println("Drawing shape")
}

class Circle : Shape() {
    override fun draw() = println("Drawing circle")
    fun radius() = 5.0
}

class Rectangle : Shape() {
    override fun draw() = println("Drawing rectangle")
}

// 多态
fun renderShape(shape: Shape) {
    shape.draw()  // 实际调用子类方法
}

// 智能类型转换(is 检查后自动转换)
fun handleShape(shape: Shape) {
    if (shape is Circle) {
        // 编译器自动转换为 Circle 类型
        println("Radius: ${shape.radius()}")
    }

    // when + smart cast
    when (shape) {
        is Circle -> println("It's a circle")
        is Rectangle -> println("It's a rectangle")
    }
}

14. 数据类与解构声明

数据类自动生成 equals()hashCode()toString()copy()componentN() 方法。

data class User(
    val id: Long,
    val name: String,
    val email: String
)

// 自动生成的功能
val user1 = User(1, "Alice", "alice@example.com")
println(user1)  // User(id=1, name=Alice, email=alice@example.com)

// copy 并修改部分属性
val user2 = user1.copy(email = "new@example.com")

// 解构声明(基于 componentN 函数)
val (id, name, email) = user1
println("$id, $name, $email")

// componentN 函数
val id = user1.component1()
val name = user1.component2()

解构声明

解构声明适用于任何提供了 componentN() 函数的类型,包括数据类、PairTripleMap.Entry 等:

// Pair 解构
val (country, city) = Pair("China", "Shanghai")

// Triple 解构
val (x, y, z) = Triple(1, 2, 3)

// Map 遍历时解构
val map = mapOf("key1" to "val1", "key2" to "val2")
for ((key, value) in map) {
    println("$key -> $value")
}

// 不需要的变量用 _ 跳过
val (_, email2) = user1  // 只要 email

⚠️ 注意:数据类的要求:主构造函数至少有一个参数;所有主构造参数必须用 valvar 声明;不能是 abstract / open / sealed / inner 类。


15. 枚举类

// 简单枚举
enum class Direction {
    NORTH, SOUTH, EAST, WEST
}

// 带属性的枚举
enum class Color(val rgb: Int) {
    RED(0xFF0000),
    GREEN(0x00FF00),
    BLUE(0x0000FF);

    fun hexString() = "#${rgb.toString(16)}"
}

// when + 枚举(编译器检查穷举)
fun describe(color: Color) = when (color) {
    Color.RED -> "红色"
    Color.GREEN -> "绿色"
    Color.BLUE -> "蓝色"
}

💡 提示:枚举类的每个值都是单例对象,适合"固定选项 + 固定属性"的场景(如星期几、方向、状态码)。


16. 密封类

密封类用于表示受限的类层次结构,所有子类在编译期已知。配合 when 使用时编译器会检查是否覆盖了所有分支。

// 密封类(受限的类层次结构)
sealed class Result {
    data class Success(val data: String) : Result()
    data class Error(val code: Int, val message: String) : Result()
    object Loading : Result()
}

// when 必须覆盖所有分支(编译器检查)
fun handleResult(result: Result) = when (result) {
    is Result.Success -> println("成功: ${result.data}")
    is Result.Error -> println("错误: ${result.message}")
    Result.Loading -> println("加载中...")
    // 不需要 else,编译器确保穷举
}
对比 枚举类 密封类
实例 每个值为单例 子类可有多个不同状态实例
数据 每个值只能有固定属性 每个子类可携带不同数据
适用场景 固定选项(星期、方向) 代数数据类型(网络结果、UI 状态)

17. 对象声明与伴生对象

// 对象声明 → 单例模式(线程安全)
object DatabaseConfig {
    val url = "jdbc:mysql://localhost:3306/mydb"
    val username = "root"
    private val password = "password"

    fun connect() {
        println("Connecting to $url")
    }
}
// 使用
DatabaseConfig.connect()

// 伴生对象 → 类的"静态"成员
class MyClass {
    companion object Factory {
        const val VERSION = "1.0.0"

        fun create(): MyClass = MyClass()

        // 生成 JVM 静态方法(Java 可直接通过类名调用)
        @JvmStatic
        fun staticMethod() = println("Static call (JVM)")

        // Kotlin 中两种方式调用一样,但 Java 中需要通过 Companion 访问
        fun instanceMethod() = println("Companion instance call")
    }
}
// 使用
MyClass.create()
MyClass.VERSION
MyClass.staticMethod()

💡 提示:在 Kotlin 中,@JvmStatic 不影响 Kotlin 调用方式;它的作用是让 Java 代码可以通过 MyClass.staticMethod() 而非 MyClass.Companion.staticMethod() 调用。


18. 扩展函数与作用域函数

扩展函数

无需继承即可为已有类添加方法:

// 扩展函数 - 为已有类添加方法
fun String.addExclamation(): String = this + "!"
println("Hi".addExclamation())  // Hi!

// 带参数的扩展函数
fun String.repeat(n: Int): String = this.repeat(n)
println("Go".repeat(3))  // GoGoGo

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

// 扩展属性
val String.wordCount: Int
    get() = this.split(" ").size

作用域函数

函数 上下文对象 返回值 典型用途
let it Lambda 结果 空安全处理、链式类型转换
apply this 上下文对象 对象初始化/配置
also it 上下文对象 附加副作用(日志等)
run this Lambda 结果 对象操作 + 返回计算结果
with this Lambda 结果 对同一个对象做多个操作
// apply:配置对象,返回对象本身
val person = Person().apply {
    name = "Tom"
    age = 30
}

// let:空安全 + 链式转换
val length = "hello".let { it.uppercase() }.let { it.length }

// also:附加副作用
val result = "hello"
    .uppercase()
    .also { println("转换结果: $it") }

// run:对象操作 + 返回计算结果
val info = person.run {
    "Name: $name, Age: $age"
}

// with:对同一个对象执行多个操作
with(person) {
    introduce()
    println(age)
}

19. 属性高级用法

class PropertyExample {
    // 自定义 getter/setter
    var customProperty: String = "default"
        get() = field.uppercase()       // field = 幕后字段
        set(value) {
            if (value.isNotEmpty()) {
                field = value.trim()
            }
        }

    // 计算属性(无幕后字段,每次访问重新计算)
    val computedProperty: Int
        get() = (1..100).random()

    // 延迟初始化(依赖注入场景)
    lateinit var lateInitProp: String

    // 惰性初始化(首次访问时才计算,线程安全)
    val lazyProp: String by lazy {
        println("首次访问才计算")
        "Expensive result"
    }
}

进阶篇


20. 中缀函数

infix 关键字标记的函数可以通过中缀表示法调用(省略点和括号),使代码更接近自然语言。

// 自定义中缀函数
infix fun String.onto(other: String) = Pair(this, other)

// 中缀调用
val pair = "key" onto "value"    // 等价于 "key".onto("value")
println(pair)  // (key, value)

// 中缀函数必须满足:
// - 是成员函数或扩展函数
// - 只有一个参数
// - 参数不能是可变参数,不能有默认值

Kotlin 内置的中缀函数(前面已经用过了):

中缀函数 示例 说明
to "key" to "value" 创建 Pair
until 1 until 5 半开区间 [1, 5)
downTo 5 downTo 1 递减区间
step 1..10 step 2 步长
// 更多自定义示例
class Person(val name: String) {
    infix fun says(message: String) {
        println("$name says: $message")
    }
}

Person("Alice") says "Hello!"  // Alice says: Hello!

21. 操作符重载

Kotlin 允许为自定义类型重载标准操作符,让自定义类型的运算像原生类型一样自然。

data class Point(val x: Int, val y: Int) {
    // 加法
    operator fun plus(other: Point) = Point(x + other.x, y + other.y)
    // 减法
    operator fun minus(other: Point) = Point(x - other.x, y - other.y)
    // 取反
    operator fun unaryMinus() = Point(-x, -y)
    // 下标访问
    operator fun get(index: Int) = when (index) {
        0 -> x
        1 -> y
        else -> throw IndexOutOfBoundsException()
    }
    // 判断包含
    operator fun contains(p: Point) = x == p.x && y == p.y
}

val p1 = Point(1, 2)
val p2 = Point(3, 4)

val p3 = p1 + p2         // Point(4, 6)
val p4 = p2 - p1         // Point(2, 2)
val neg = -p1            // Point(-1, -2)
val x = p1[0]            // 1(下标访问)
val hasPoint = p1 in listOf(p1, p2)  // contains

常用可重载操作符

操作符 函数名 示例
+ plus a + b
- minus a - b
* times a * b
/ div a / b
% rem a % b
++ inc a++
-- dec a--
[] get / set a[i] / a[i] = v
in contains a in b
() invoke a()
== equals a == b
> < compareTo a > b
.. rangeTo a..b

22. 高阶函数与 Lambda

高阶函数是指接受函数作为参数或返回函数的函数。这是函数式编程的核心。

// 函数类型作为参数
fun operateOnNumbers(a: Int, b: Int, operation: (Int, Int) -> Int): Int {
    return operation(a, b)
}

// 使用 Lambda
val sum = operateOnNumbers(5, 3) { x, y -> x + y }      // 8
val product = operateOnNumbers(5, 3) { x, y -> x * y }  // 15

// 函数引用
fun isEven(num: Int) = num % 2 == 0
val numbers = listOf(1, 2, 3, 4)
val evens = numbers.filter(::isEven)  // [2, 4]

// 返回函数的函数
fun makeMultiplier(factor: Int): (Int) -> Int {
    return { x -> x * factor }
}
val triple = makeMultiplier(3)
println(triple(10))  // 30
Lambda 语法 含义
{ x -> x * 2 } 单参数,显式命名
{ it * 2 } 单参数,隐式名称 it
{ x, y -> x + y } 多参数
::functionName 函数引用
this::methodName 成员方法引用
ClassName::methodName 类方法引用

内联函数

inline 将 Lambda 体直接嵌入调用处,避免创建匿名对象,减少运行时开销:

// 内联函数
inline fun measureTime(action: () -> Unit) {
    val start = System.currentTimeMillis()
    action()
    println("耗时: ${System.currentTimeMillis() - start}ms")
}

// 不希望内联的 Lambda 参数用 noinline
inline fun execute(inlineBlock: () -> Unit, noinline noInlineBlock: () -> Unit) {
    inlineBlock()      // 内联
    noInlineBlock()    // 不内联,可以作为参数传递
}

// crossinline:禁止非局部返回(内联 + 不允许 return)
inline fun safeRun(crossinline block: () -> Unit) {
    val runnable = Runnable { block() }  // 跨执行上下文时使用
    runnable.run()
}
修饰符 作用
inline Lambda 内联到调用处,允许非局部 return
noinline 指定某个 Lambda 参数不内联
crossinline 内联但禁止非局部 return(跨执行上下文时)

23. 类型别名

typealias 为现有类型创建别名,提高代码可读性。

// 函数类型别名
typealias ClickHandler = (View) -> Unit
typealias Predicate<T> = (T) -> Boolean

// 泛型类型别名
typealias UserMap = Map<Long, User>
typealias StringTable = Map<String, Map<String, String>>

// 内部类别名
typealias Handler = MyClass.MyInnerClass

// 嵌套类别名
typealias NodeSet = Set<Network.Node>

// 使用(与原名完全等价)
fun setClickListener(handler: ClickHandler) { /* ... */ }
val users: UserMap = mapOf()

24. 类型系统:Any / Unit / Nothing

Kotlin 的类型层次非常简洁清晰:

            Any? (可空类型的根)
           /    \
       Any      Nothing?
        |         |
    (所有非空类型)  Nothing (所有类型的子类型)
        |
    ┌───┼───┬──────┬─────┐
  Int String List  Boolean  ...
  • Any:所有非空类型的根,等价于 Java 的 Object(但不含 wait/notify
  • Any?:所有可空类型的根
  • Nothing:所有类型的子类型(底类型),表示"永不存在的值"
// Any —— 所有非空类型的超类
val any: Any = "hello"
val any2: Any = 42

// Unit —— 对应 Java void(但 Unit 是真正的对象)
fun log(msg: String): Unit {          // Unit 返回类型可省略
    println(msg)
}
val unit: Unit = log("hi")            // Unit 只有唯一实例

// Nothing —— 永远不会返回(throw 或无限循环)
fun fail(message: String): Nothing = throw IllegalArgumentException(message)
fun infiniteLoop(): Nothing {
    while (true) { /* ... */ }
}

// TODO() 的返回类型就是 Nothing(占位用)
fun notYetImplemented(): String = TODO("待实现")

// Nothing 是任何类型的子类型,所以可以用于:
val x: String = fail("error")  // 编译通过!Nothing 可以赋值给任何类型
val y: String? = null          // null 的类型是 Nothing?

💡 提示:理解 Nothing 有助于理解空安全——null 的类型是 Nothing?,因此可以赋值给任何可空类型。TODO() 返回 Nothing,因此可以放在任何表达式位置充当占位符。


实用篇


25. 文件操作

Kotlin 对 java.io.File 做了大量扩展,读写文件比 Java 简洁很多。

import java.io.File

// 读取整个文件为字符串
val content = File("input.txt").readText()

// 写入字符串到文件
File("output.txt").writeText("Hello, Kotlin!")

// 按行读取(处理大文件时不会一次性加载全部内容)
File("data.txt").useLines { lines ->
    lines.filter { it.isNotBlank() }
         .forEach { println(it) }
}

// 遍历目录树
File("src").walk()
    .filter { it.extension == "kt" }
    .forEach { println(it.name) }

// 复制文件
File("source.txt").copyTo(File("dest.txt"), overwrite = true)

// 递归删除目录
File("temp").deleteRecursively()

// use 自动关闭资源(替代 Java 的 try-with-resources)
File("data.txt").bufferedReader().use { reader ->
    reader.forEachLine { println(it) }
}

// 创建临时文件
val tempFile = File.createTempFile("prefix", ".tmp")
tempFile.deleteOnExit()

大文件复制 / 移动(带进度监听)

copyTodeleteRecursively 是一次性操作,无法监听进度。处理大文件时,可以手动分块读写实现进度回调:

import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream

// 带进度回调的大文件复制
fun File.copyWithProgress(
    dest: File,
    bufferSize: Int = 8 * 1024,                     // 8KB 缓冲区
    onProgress: (copied: Long, total: Long) -> Unit  // 进度回调
) {
    val total = this.length()
    var copied = 0L

    FileInputStream(this).use { input ->
        FileOutputStream(dest).use { output ->
            val buffer = ByteArray(bufferSize)
            var bytesRead: Int
            while (input.read(buffer).also { bytesRead = it } != -1) {
                output.write(buffer, 0, bytesRead)
                copied += bytesRead
                onProgress(copied, total)           // 通知调用方
            }
        }
    }
}

// 使用示例
val source = File("large_file.iso")
val dest = File("copy.iso")

source.copyWithProgress(dest) { copied, total ->
    val percent = (copied * 100 / total)
    val copiedMB = copied / 1024 / 1024
    val totalMB = total / 1024 / 1024
    print("\r进度: $percent%  ($copiedMB / $totalMB MB)")  // \r 覆盖当前行
}
println()  // 换行

💡 提示:移动文件本质上就是 copyWithProgress + 删除源文件。如果需要更丰富的进度信息(速度、剩余时间等),可以用 kotlinx.coroutinesflow 将进度做成 Flow<Progress>,在协程中驱动 UI 更新。


26. 正则表达式

Kotlin 的原始字符串("""...""")与正则结合是绝配——无需像 Java 那样疯狂转义反斜杠。

// 创建正则(原始字符串避免转义)
val phoneRegex = Regex("""1[3-9]\d{9}""")
val dateRegex = """\d{4}-\d{2}-\d{2}""".toRegex()

// 判断是否包含匹配
val hasPhone = phoneRegex.containsMatchIn("手机: 13812345678")  // true

// 查找第一个匹配
val match = phoneRegex.find("A: 13811112222, B: 13933334444")
println(match?.value)  // 13811112222

// 查找所有匹配
phoneRegex.findAll("A: 13811112222, B: 13933334444").forEach {
    println(it.value)
}

// 分组捕获(括号分组)
val birthdayRegex = """(\d{4})-(\d{2})-(\d{2})""".toRegex()
val result = birthdayRegex.find("生日: 1995-08-20")
result?.let {
    val (year, month, day) = it.destructured
    println("${year}年${month}月${day}日")  // 1995年08月20日
}
// groupValues 索引 0 是完整匹配,1/2/3 是分组
println(result?.groupValues?.get(1))  // 1995

// 替换
val masked = phoneRegex.replace("13812345678") {
    it.value.replaceRange(3, 7, "****")
}
println(masked)  // 138****5678

// 拆分字符串
val words = Regex("""\s+""").split("Kotlin  is    great")
println(words)  // [Kotlin, is, great]

27. JSON 序列化

Kotlin 官方提供的 kotlinx.serialization 是编译期安全的序列化方案,无需反射。

依赖:org.jetbrains.kotlinx:kotlinx-serialization-json 插件:kotlin("plugin.serialization")

import kotlinx.serialization.Serializable
import kotlinx.serialization.encodeToString
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import kotlinx.serialization.SerialName

// 标记可序列化的数据类
@Serializable
data class User(
    val id: Int,
    val name: String,
    val email: String
)

// 序列化:对象 → JSON 字符串
val user = User(1, "Alice", "alice@example.com")
val json = Json.encodeToString(user)
println(json)  // {"id":1,"name":"Alice","email":"alice@example.com"}

// 反序列化:JSON 字符串 → 对象
val jsonStr = """{"id":2,"name":"Bob","email":"bob@example.com"}"""
val parsed = Json.decodeFromString<User>(jsonStr)
println(parsed.name)  // Bob

// 自定义 JSON 配置
val prettyJson = Json {
    prettyPrint = true          // 格式化输出
    ignoreUnknownKeys = true    // 忽略 JSON 中多出的字段
    isLenient = true            // 宽松模式(允许 JSON 注释等)
    encodeDefaults = true       // 输出默认值(否则不写)
}
println(prettyJson.encodeToString(user))
// {
//     "id": 1,
//     "name": "Alice",
//     "email": "alice@example.com"
// }

// 字段名映射(驼峰 ↔ 蛇形)
@Serializable
data class ApiUser(
    @SerialName("user_id") val id: Int,
    @SerialName("user_name") val name: String,
    @SerialName("is_active") val isActive: Boolean
)
val apiJson = """{"user_id":1,"user_name":"Alice","is_active":true}"""
val apiUser = Json.decodeFromString<ApiUser>(apiJson)
println(apiUser.isActive)  // true

// 嵌套对象 + 列表
@Serializable
data class ApiResponse(
    val code: Int,
    val message: String,
    val data: List<User>
)

28. 日期时间

Kotlin 直接使用强大的 java.time 包,比老旧的 DateCalendar 好用得多。

import java.time.*
import java.time.format.DateTimeFormatter

// 获取当前时间
val now = LocalDateTime.now()
val today = LocalDate.now()
val currentTime = LocalTime.now()

// 创建指定日期
val date = LocalDate.of(2024, 3, 15)
val date2 = LocalDate.parse("2024-03-15")

// 日期运算
val tomorrow = today.plusDays(1)
val lastWeek = today.minusWeeks(1)
val nextMonth = today.plusMonths(1)
val isAfter = date.isAfter(today)     // 日期比较

// 格式化输出
val formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss")
println(now.format(formatter))  // 2024年03月15日 14:30:00

// 解析字符串
val parsed = LocalDate.parse("2024-03-15", DateTimeFormatter.ISO_LOCAL_DATE)

// 计算两个日期之间的间隔
val period = Period.between(date, today)
println("${period.years}年${period.months}月${period.days}天")

// 精确时间差
val start = Instant.now()
// ... 执行操作 ...
val end = Instant.now()
val duration = Duration.between(start, end)
println("耗时: ${duration.toMillis()}ms")

// 时区处理
val shanghaiNow = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"))
val tokyoTime = shanghaiNow.withZoneSameInstant(ZoneId.of("Asia/Tokyo"))
println("上海: $shanghaiNow, 东京: $tokyoTime")

29. 网络请求(Ktor Client)

Ktor 是 Kotlin 官方的 HTTP 客户端,基于协程设计,轻量且简洁。

依赖:io.ktor:ktor-client-core + 引擎(如 io.ktor:ktor-client-cioktor-client-okhttp

import io.ktor.client.*
import io.ktor.client.request.*
import io.ktor.client.statement.*
import io.ktor.http.*
import kotlinx.coroutines.*

val client = HttpClient()

// GET 请求
suspend fun fetchData(): String {
    val response: HttpResponse = client.get("https://api.example.com/data")
    return response.bodyAsText()
}

// GET 请求(带查询参数和 Header)
suspend fun search(query: String): String {
    val response = client.get("https://api.example.com/search") {
        parameter("q", query)                   // ?q=kotlin
        parameter("page", 1)                    // &page=1
        header("Authorization", "Bearer token_xxx")
    }
    return response.bodyAsText()
}

// POST 请求(发送 JSON)
@Serializable
data class CreateUser(val name: String, val email: String)

suspend fun createUser(user: CreateUser): String {
    val response = client.post("https://api.example.com/users") {
        contentType(ContentType.Application.Json)
        setBody(user)                           // 自动序列化为 JSON
    }
    return response.bodyAsText()
}

// 上传文件
suspend fun uploadFile(file: File): String {
    val response = client.submitFormWithBinaryData(
        url = "https://api.example.com/upload",
        formData = formData {
            append("file", file.readBytes(), Headers.build {
                append(HttpHeaders.ContentDisposition, "filename=\"${file.name}\"")
            })
        }
    )
    return response.bodyAsText()
}

// 使用
fun main() = runBlocking {
    val data = fetchData()
    println(data)
    client.close()  // 程序退出前关闭客户端
}

高级篇


30. 委托模式 *

类委托

interface Repository {
    fun save(data: String)
    fun load(): String
}

class DatabaseRepository : Repository {
    override fun save(data: String) = println("Saving: $data")
    override fun load(): String = "Data from DB"
}

// 委托给另一个对象,可选择性重写
class CacheRepository(db: Repository) : Repository by db {
    override fun save(data: String) {
        println("Caching: $data")
        // db.save(data)  // 也可以调用被委托对象
    }
}

属性委托

import kotlin.properties.Delegates

class Person {
    // 可观察属性
    var name: String by Delegates.observable("<no name>") {
        _, old, new -> println("$old → $new")
    }

    // 可否决属性
    var age: Int by Delegates.vetoable(0) {
        _, _, new -> new >= 0  // 拒绝负数
    }

    // 延迟初始化
    val lazyValue: String by lazy {
        println("计算一次")
        "Result"
    }
}

31. 泛型高级用法 *

// 协变(out):只能作为返回值(生产者)
interface Producer<out T> {
    fun produce(): T
}

// 逆变(in):只能作为参数(消费者)
interface Consumer<in T> {
    fun consume(item: T)
}

// 星投影
fun printList(list: List<*>) {
    list.forEach { println(it) }
}

// reified 具体化类型参数(需 inline)
inline fun <reified T> isInstance(value: Any): Boolean {
    return value is T
}
// 使用:T 在运行时可见
val result = isInstance<String>("hello")  // true

// 泛型约束
fun <T : Comparable<T>> sort(list: List<T>) { /* ... */ }

// 多个约束(where 子句)
fun <T> copyIfGreater(list: List<T>, threshold: T): List<T>
    where T : Comparable<T>, T : Cloneable {
    return list.filter { it > threshold }
}
修饰符 名称 规则 示例
out T 协变 只能出现在输出位置(返回值) Producer<out T>
in T 逆变 只能出现在输入位置(参数) Consumer<in T>
不变 默认 同时出现在输入和输出 List<T>

32. 协程(Coroutines)

协程是 Kotlin 的轻量级并发解决方案。一个线程可以运行成千上万个协程,协程的切换开销远小于线程。

依赖:org.jetbrains.kotlinx:kotlinx-coroutines-core

基础用法

import kotlinx.coroutines.*

// 基础:launch 启动一个协程
fun main() = runBlocking {
    launch {
        delay(1000L)        // 非阻塞挂起
        println("World!")
    }
    println("Hello,")
}
// 输出:Hello, →(等1秒)→ World!

// suspend 挂起函数
suspend fun fetchData(): String {
    delay(2000L)
    return "数据加载完成"
}

// 异步并发:async + await
suspend fun loadAll() = coroutineScope {
    val job1 = async { fetchUserInfo() }
    val job2 = async { fetchUserPosts() }

    val user = job1.await()
    val posts = job2.await()
    return@coroutineScope Pair(user, posts)
}

调度器与上下文

// 常用调度器
launch(Dispatchers.IO) {        // IO 密集型(网络/文件)
    val data = fetchFromNetwork()
}

launch(Dispatchers.Main) {      // UI 线程(Android)
    updateUI(data)
}

launch(Dispatchers.Default) {   // CPU 密集型计算
    val result = heavyComputation()
}

// 自定义线程池
val customContext = newSingleThreadContext("CustomThread")
launch(customContext) { /* ... */ }

Flow —— 响应式流

// 创建 Flow
fun simpleFlow(): Flow<Int> = flow {
    for (i in 1..3) {
        delay(100)
        emit(i)  // 发射数据
    }
}

// 收集 Flow
runBlocking {
    simpleFlow().collect { value ->
        println(value)  // 1, 2, 3(间隔100ms)
    }
}

// Flow 操作符
simpleFlow()
    .map { it * 2 }
    .filter { it > 2 }
    .onEach { println("Processing: $it") }
    .catch { e -> println("Error: $e") }
    .collect()
概念 说明
CoroutineScope 协程作用域,管理协程生命周期
launch 启动协程,不返回结果(返回 Job)
async 启动协程,返回 Deferred(可通过 await 获取结果)
suspend 挂起函数标记,可在协程中调用
delay 非阻塞延迟(挂起函数)
Flow 冷流,类似响应式流(RxJava 的 Observable)
Channel 协程间通信(类似 BlockingQueue)
Mutex 协程互斥锁

33. DSL 构建器 *

Kotlin 的 Lambda 与接收者类型结合,可以构建优雅的领域特定语言(DSL)。

// HTML DSL 示例
fun html(init: HTML.() -> Unit): HTML {
    val html = HTML()
    html.init()
    return html
}

class HTML {
    private val children = mutableListOf<String>()

    fun body(init: Body.() -> Unit) {
        val body = Body()
        body.init()
        children.add("<body>${body.content}</body>")
    }

    override fun toString() = "<html>${children.joinToString("")}</html>"
}

class Body {
    var content: String = ""
    fun h1(text: String) { content += "<h1>$text</h1>" }
    fun p(text: String) { content += "<p>$text</p>" }
}

// 使用 DSL —— 像写 HTML 一样自然
val page = html {
    body {
        h1("标题")
        p("段落内容")
    }
}

34. 注解与反射

// 自定义注解
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
annotation class MyAnnotation(val description: String)

// 使用反射
import kotlin.reflect.full.*

@MyAnnotation("测试类")
class TestClass {
    @MyAnnotation("测试方法")
    fun testMethod() {}
}

fun analyze(obj: Any) {
    val kClass = obj::class
    val annotation = kClass.findAnnotation<MyAnnotation>()
    println(annotation?.description)

    kClass.memberFunctions.forEach { function ->
        val funcAnnotation = function.findAnnotation<MyAnnotation>()
        println("方法: ${function.name}, 描述: ${funcAnnotation?.description}")
    }
}

35. Kotlin 与 Java 互操作 *

Kotlin 与 Java 可以无缝互调用,是实际项目中的必备知识。

从 Java 调用 Kotlin

// Kotlin 侧 —— 为 Java 调用优化
class KotlinLib {
    companion object {
        // 生成真正的 JVM 静态方法
        @JvmStatic
        fun create(): KotlinLib = KotlinLib()

        // 普通伴生对象方法(Java 需通过 Companion 调用)
        fun helper() = println("helper")
    }

    // 为有默认参数的函数生成重载方法
    @JvmOverloads
    fun greet(name: String = "World", times: Int = 1) {
        repeat(times) { println("Hello, $name!") }
    }

    // 将属性暴露为 JVM 字段(而非 getter/setter)
    @JvmField
    val CONSTANT = 42

    // 修改生成的 Java 方法名(解决签名冲突)
    @JvmName("getFullName")
    fun getName(): String = "full name"
}
// Java 侧调用
KotlinLib.create();           // @JvmStatic 生效
KotlinLib.Companion.helper(); // 无注解需通过 Companion
KotlinLib lib = new KotlinLib();
lib.greet("Alice");           // @JvmOverloads 生成重载
lib.greet("Alice", 3);
int value = KotlinLib.CONSTANT; // @JvmField 直接访问字段

从 Kotlin 调用 Java

// 平台类型 String! —— 空安全信息未知
val result = someJavaMethod()   // 返回类型是 String!(平台类型)
// 你需要自己判断是否可空:
val safe: String? = result      // 当作可空处理
val assert: String = result     // 当作非空处理(result 为 null 时抛异常)

// SAM 转换(单抽象方法接口自动转为 Lambda)
// Java 接口:
// public interface OnClickListener {
//     void onClick(View v);
// }
button.setOnClickListener { view -> println("Clicked!") }

// 访问 Java getter/setter(Kotlin 自动识别为属性访问)
val name = file.getName()       // Java 写法
val name = file.name            // Kotlin 属性语法(等价)

常用互操作注解速查

注解 作用
@JvmStatic 伴生对象方法生成真正的静态方法
@JvmOverloads 为默认参数生成 Java 重载方法
@JvmField 属性暴露为直接字段访问
@JvmName 修改生成的字节码方法名
@JvmSynthetic 对 Java 隐藏该方法
@Throws 声明可能抛出的受检异常

36. Kotlin Multiplatform *

// commonMain —— 公共代码
expect fun platformName(): String
expect class PlatformLogger {
    fun log(message: String)
}

// androidMain —— Android 实现
actual fun platformName(): String = "Android"
actual class PlatformLogger {
    actual fun log(message: String) {
        Log.d("KMP", message)
    }
}

// iosMain —— iOS 实现
actual fun platformName(): String = "iOS"
actual class PlatformLogger {
    actual fun log(message: String) {
        println("[iOS] $message")
    }
}