Kotlin
8/3/26About 14 min
说明
- 本文是 Kotlin 知识点系统总结,适合已经熟悉 Java 的开发者快速掌握 Kotlin
- 更多参考资料:Kotlin 官方文档、Kotlin 中文站
一、Kotlin 概述 ⭐
1. 为什么学习 Kotlin
Kotlin 是 JetBrains 公司开发的运行在 JVM 上的静态类型编程语言,2017 年被 Google 指定为 Android 官方开发语言。它的出现主要是为了解决 Java 的痛点:
| Java 的痛点 | Kotlin 的解决方式 |
|---|---|
| 空指针异常(NPE)频发 | 空安全类型系统,编译期杜绝大部分 NPE |
| 代码冗长(getter/setter、样板代码) | 属性、数据类、默认参数等语法糖 |
| 缺少函数式编程支持(Lambda 不够爽) | 一等函数、高阶函数、集合函数式 API |
| 无法轻易扩展已有类 | 扩展函数、属性委托 |
| 单例、静态等实现繁琐 | object、伴生对象 |
| 并发编程门槛高 | 协程(Coroutines)轻量级并发 |
2. Kotlin 的特性
- 100% 兼容 Java:可与 Java 混编,无缝调用 Java 库
- 静态类型:编译期类型检查,但支持类型推断
- 函数式编程:函数是一等公民,支持 Lambda、高阶函数
- 空安全:
?、?:、!!等操作符从源头消灭 NPE - 多平台:JVM、Android、Native、JS、多平台共享代码
- Kotlin Multiplatform (KMP):一套业务代码,多端复用
3. 程序入口
// 主函数(顶层函数,无需类包裹)
fun main(args: Array<String>) {
println("Hello Kotlin")
}
// 简化写法
fun main() {
println("Hello Kotlin")
}二、基础语法 ⭐
1. 变量与常量
Kotlin 使用 var(可变)和 val(只读)声明变量,类型可推断:
var name = "张三" // 可变变量(类型推断为 String)
val age = 18 // 只读变量,相当于 final,赋值后不可改
val score: Double = 99.5 // 显式指定类型
var x: Int? = null // 可空类型建议
- 优先使用
val,只有当变量需要被重新赋值时才用var - 只读变量(
val)≠ 不可变:对于集合它只是引用不可变,指向的对象仍可改变
2. 数据类型
Kotlin 中没有基本类型和包装类型之分,都是对象,但编译期会做优化。
| 类型 | 说明 | 示例 |
|---|---|---|
Byte | 8 位 | val b: Byte = 127 |
Short | 16 位 | val s: Short = 300 |
Int | 32 位 | val i = 100 |
Long | 64 位 | val l = 100L |
Float | 32 位浮点 | val f = 1.5f |
Double | 64 位浮点 | val d = 1.5 |
Char | 字符 | val c = 'A' |
Boolean | 布尔 | val flag = true |
String | 字符串 | val s = "hello" |
3. 字符串与模板表达式
val name = "Kotlin"
val version = 1.9
// 字符串模板($ 变量、${} 表达式)
println("语言: $name") // 语言: Kotlin
println("信息: ${name} v$version") // 信息: Kotlin v1.9
println("求和: ${1 + 2}") // 求和: 3
// 三引号原始字符串(多行、无需转义)
val raw = """
|你好,
|这是多行字符串
|使用 trimMargin() 去掉前缀
""".trimMargin()4. 流程控制
// if 可以作为表达式返回结果(没有三元运算符)
val max = if (a > b) a else b
// when 替代 switch,且可以当作表达式
fun describe(obj: Any): String = when (obj) {
1 -> "One"
"Hello" -> "Greeting"
is Long -> "Long 类型"
!is String -> "Not a string"
else -> "Unknown"
}
// for 循环
for (i in 1..5) println(i) // 1..5 闭区间 [1,5]
for (i in 1 until 5) println(i) // until 左闭右开 [1,5)
for (i in 5 downTo 1 step 2) println(i) // 倒序步长
for (ch in "abcd") println(ch) // 遍历字符
for ((i, v) in list.withIndex()) println("$i -> $v")
// while / do-while 与 Java 相同5. 运算符
- 算术、赋值、比较运算符与 Java 一致
- Kotlin 中
==比较的是结构相等(相当于 Java 的equals),===才是引用相等 - 支持
in/!in判断包含关系,rangeTo区间
三、函数 ⭐
1. 函数定义
// 普通函数
fun add(a: Int, b: Int): Int {
return a + b
}
// 单表达式函数(省略返回类型和 return)
fun add(a: Int, b: Int) = a + b
// 无返回值(Unit 可省略)
fun printHello(): Unit { println("Hello") }2. 默认参数与命名参数
// 默认参数
fun greeting(name: String, prefix: String = "你好") {
println("$prefix, $name")
}
greeting("张三") // 你好, 张三
greeting("张三", "Hello") // Hello, 张三
// 命名参数(跳过中间参数,提高可读性)
fun build(width: Int, height: Int, color: String = "red")
build(width = 100, height = 200)3. Lambda 表达式
// Lambda 完整写法
val sum: (Int, Int) -> Int = { x, y -> x + y }
// 简写:it 代表唯一参数
val list = listOf(1, 2, 3, 4)
list.filter { it % 2 == 0 } // [2, 4]
list.map { it * it } // [1, 4, 9, 16]
list.reduce { acc, i -> acc + i } // 104. 高阶函数
高阶函数是指参数或返回值是函数的函数:
// 参数是函数
fun operate(x: Int, y: Int, op: (Int, Int) -> Int): Int {
return op(x, y)
}
operate(2, 3) { a, b -> a * b } // 6(Lambda 拖尾语法)
// 返回值是函数
fun addOffset(offset: Int): (Int) -> Int = { it + offset }
val add10 = addOffset(10)
add10(5) // 155. 内联函数 inline
// inline:编译期把函数体和 Lambda 直接展开到调用处,避免创建匿名类对象
// 返回值:return 直接返回外部函数(非局部返回,@标签可做局部返回);crossinline 禁止非局部返回
inline fun <T> repeatAction(times: Int, action: (Int) -> T) {
for (i in 0 until times) action(i)
}
// 非局部返回示例
fun test() {
repeatAction(3) {
if (it == 1) return // ✅ 直接返回 test(),不只是 Lambda
}
println("不会打印") // 被跳过
}
// 局部返回(@标签)
fun test2() {
repeatAction(3) {
if (it == 1) return@repeatAction // ✅ 只返回当前 Lambda
}
println("会打印") // 继续执行
}
// noinline:不想内联某个 Lambda 参数时使用
// crossinline:禁止 Lambda 中 return 非局部返回6. 扩展函数
在不修改源码的情况下为类添加新方法,是 Kotlin 最强大的特性之一:
// 为 String 扩展一个方法
fun String.lastChar(): Char = this[this.length - 1]
println("Kotlin".lastChar()) // n
// 标准库中大量使用扩展函数
"abc".reversed()
list.map { ... }.filter { ... }注意
- 扩展函数本质是静态方法,没有重写机制(不是成员函数)
- 若成员函数和扩展函数同名,成员函数优先级更高
四、空安全 ⭐
空安全是 Kotlin 的核心特性,目的是消灭空指针异常(NPE)。
1. 可空类型与非空类型
var name: String = "abc" // 非空类型,不能赋 null
var name2: String? = null // 可空类型,可以赋 null
// 编译错误!非空类型不能赋值 null
// name = null2. 空安全操作符
var b: String? = "abc"
// ?. 安全调用:对象为 null 时返回 null,不抛异常
println(b?.length) // 3
b = null
println(b?.length) // null
// ?: Elvis 操作符(空合并):为 null 时返回默认值
val len = b?.length ?: -1 // b 为 null 时返回 -1
// !! 非空断言:明知不为空时使用,为 null 直接抛 NPE
val len2 = b!!.length // 危险操作,慎用
// 链式调用
user?.address?.city?.length
// 类型智能转换(Smart Cast)
if (b != null) {
// 此处 b 已被自动转换为非空类型
println(b.length)
}3. 作用域函数
Kotlin 标准库提供 5 个作用域函数,用于在对象上下文中执行代码:
| 函数 | 上下文对象 | 返回值 | 适用场景 |
|---|---|---|---|
let | it | Lambda 结果 | 空安全 + 对结果赋值 |
run | this | Lambda 结果 | 初始化对象并返回结果 |
with | this | Lambda 结果 | 对同一对象多次调用方法 |
apply | this | 对象本身 | 配置对象属性(链式) |
also | it | 对象本身 | 附加操作(如日志) |
// let:配合空安全使用
val result = user?.let {
it.name + " 登录成功"
} ?: "未登录"
// apply:配置对象
val person = Person().apply {
name = "张三"
age = 18
}
// run
val info = "Hello".run { length }
// with:不引入新变量地连续调用
val text = with(StringBuilder()) {
append("Hello ")
append("World")
toString()
}
// also:链式调用中插入副作用
list.filter { ... }
.also { println("过滤后: $it") }
.map { ... }五、面向对象 ⭐
1. 类与属性
// 主构造函数直接在类名后声明
class Person(val name: String, var age: Int) {
// 初始化块(主构造函数体),可以有多个
init {
println("Person created: $name")
}
// 次构造函数(必须委托给主构造函数)
constructor(name: String) : this(name, 0)
// 成员方法
fun introduce() = "我叫 $name,今年 $age 岁"
}val属性自动生成 getter;var属性自动生成 getter + setter- 可以直接通过属性名访问,替代 Java 的
getXxx()/setXxx() - 自定义 getter/setter:
var name = "张三"; get() = field.uppercase()
2. 继承与抽象类
// 默认类都是 final,继承需要 open 修饰
open class Animal(val name: String) {
open fun sound() = "..." // 可重写的方法也要 open
}
class Dog(name: String) : Animal(name) {
override fun sound() = "汪!"
}override:重写必须显式标记abstract:抽象类interface:接口可以包含属性声明和默认实现(Kotlin 1.4+ 的 SAM 转换支持函数式接口)
interface Flyable {
val speed: Int
fun fly() // 抽象方法
fun getType() = "飞行生物" // 默认实现
}3. 数据类 data class
自动生成 equals()、hashCode()、toString()、copy() 和结构相等:
data class User(val id: Int, val name: String)
val u1 = User(1, "张三")
val u2 = u1.copy(name = "李四") // copy 可以只改部分属性
println(u1) // User(id=1, name=张三)
println(u1 == User(1, "张三")) // true,结构相等4. 密封类 sealed class
受限的类层次结构,所有子类必须与密封类在同一文件/同一模块中:
sealed class Result {
data class Success(val data: String) : Result()
data class Error(val msg: String) : Result()
}
// when 处理时无需 else,编译器保证穷尽
fun handle(r: Result) = when (r) {
is Result.Success -> "成功: ${r.data}"
is Result.Error -> "失败: ${r.msg}"
}5. 单例与伴生对象
// object 声明:真正的单例,线程安全
object DatabaseManager {
fun connect() = "连接数据库"
}
DatabaseManager.connect()
// 伴生对象:替代 Java 的 static
class MyClass {
companion object {
const val VERSION = "1.0" // const 编译期常量
fun create() = MyClass()
}
}
MyClass.VERSION
MyClass.create()6. 泛型与协变
// 泛型类
class Box<T>(val value: T)
// 泛型函数
fun <T> List<T>.lastOrNull(): T? = if (isEmpty()) null else last()
// ====== 协变与逆变(Variance)======
// 1. 协变 out(Producer - 只读)
interface Source<out T> {
fun get(): T // ✅ 只能生产 T,不能消费 T
// fun set(t: T) // ❌ 不允许,违反协变
}
// 协变示例:
val intSource: Source<Int> = object : Source<Int> {
override fun get() = 42
}
val anySource: Source<Any> = intSource // ✅ 协变:Int 是 Any 的子类,Source<Int> 可赋给 Source<Any>
// Java 对比:通配符 ? extends
// Java: List<? extends Number> list = new ArrayList<Integer>();
// Kotlin: val list: List<out Number> = listOf(1, 2, 3)
// 2. 逆变 in(Consumer - 只写)
interface Sink<in T> {
fun set(value: T) // ✅ 只能消费 T,不能生产 T
// fun get(): T // ❌ 不允许,违反逆变
}
// 逆变示例:
val anySink: Sink<Any> = object : Sink<Any> {
override fun set(value: Any) {}
}
val intSink: Sink<Int> = anySink // ✅ 逆变:Sink<Any> 可赋给 Sink<Int>
// Java 对比:通配符 ? super
// Java: List<? super Integer> list = new ArrayList<Number>();
// Kotlin: val list: MutableList<in Int> = mutableListOf<Number>()
// 3. 不变(Invariant - 既读又写)
class Box<T> {
fun get(): T? = null
fun set(value: T) {}
}
// ❌ 不变:Box<Int> 不能赋给 Box<Any>
// val box: Box<Any> = Box<Int>() // 编译错误
// Java 对比:Box<Integer> 既不能赋给 Box<Number> 也不能反之
// 4. 实际应用对比
// Kotlin(明确声明):
fun printSource(source: Source<Any>) {
println(source.get())
}
printSource(intSource) // ✅ 可以传 Source<Int>
// Java(使用通配符):
// void printSource(Source<? extends Object> source) {
// System.out.println(source.get());
// }
// 5. 星投影(类型擦除)
val mystery: List<*> = listOf(1, "a", 3.14) // List<Any?> 最宽松的类型
mystery.forEach { println(it) } // 只能当作 Any? 处理
// Java 对比:原始类型 List(无泛型信息)
// List mystery = Arrays.asList(1, "a", 3.14); // 类型不安全六、集合与标准库 ⭐
1. 集合类型
val list = listOf(1, 2, 3) // 不可变列表
val mutableList = mutableListOf(1, 2, 3) // 可变列表
val set = setOf(1, 2, 3) // 不可变集合
val map = mapOf("a" to 1, "b" to 2) // 不可变 Map
val array = arrayOf(1, 2, 3) // 数组可变性
- Kotlin 集合接口分只读(
List、Set、Map)和可变(MutableList等) - 只读集合不保证线程安全,只是不能通过该引用修改
2. 集合函数式 API
val nums = listOf(1, 2, 3, 4, 5, 6)
nums.filter { it % 2 == 0 } // 过滤:[2, 4, 6]
nums.map { it * 10 } // 映射:[10, 20, ...]
nums.reduce { acc, i -> acc + i } // 累加:21
nums.fold(100) { acc, i -> acc + i } // 带初始值累加:121
nums.any { it > 5 } // 是否存在:true
nums.all { it > 0 } // 是否全部:true
nums.count { it > 3 } // 计数:3
nums.firstOrNull { it > 10 } // 查找:null
nums.groupBy { it % 2 } // 分组
nums.associateBy { it } // 转 Map
nums.zip(listOf("a", "b")) // 拉链合并
nums.take(3) // 取前 3 个
nums.chunked(2) // 分块
nums.sum() // 求和3. 序列 Sequence
大量数据链式操作时使用,惰性求值,避免创建中间集合:
val result = (1..1_000_000).asSequence()
.filter { it % 2 == 0 }
.map { it * it }
.take(10)
.toList() // 只处理到凑够 10 个结果为止七、协程 Coroutines ⭐
协程是 Kotlin 提供的轻量级线程,用于简化异步与并发编程,挂起而非阻塞。
1. 核心概念
| 概念 | 说明 |
|---|---|
suspend | 挂起函数,只能在协程或其他挂起函数中调用 |
launch | 启动一个协程,返回 Job,不阻塞当前线程 |
async | 启动一个协程并返回 Deferred(带返回值) |
runBlocking | 阻塞当前线程等待协程完成(一般仅用于测试/main) |
CoroutineScope | 协程作用域,控制协程生命周期 |
Dispatchers | 调度器:Main、IO、Default、Unconfined |
2. 基本使用
import kotlinx.coroutines.*
fun main() = runBlocking {
// launch:并发执行,不阻塞
launch {
delay(1000) // 挂起 1 秒(不阻塞线程)
println("世界")
}
println("你好") // 先打印
delay(1500) // 等待上面协程完成
}
// async:获取返回值
val job: Deferred<Int> = async {
delay(1000)
42
}
println(job.await()) // 423. 结构化并发
fun main() = runBlocking {
val job = launch {
repeat(5) { i ->
delay(500)
println("任务 $i")
}
}
delay(1200)
job.cancel() // 取消协程
job.join() // 等待协程结束
}八、其他进阶特性
1. 解构声明
val (a, b) = Pair(1, "二") // 解构 Pair
val (x, y, z) = Triple(1, 2, 3) // 解构 Triple
data class Point(val x: Int, val y: Int)
val (px, py) = Point(10, 20) // 数据类自带 componentN()
for ((key, value) in map) {
println("$key -> $value")
}2. 运算符重载
data class Money(val amount: Int) {
operator fun plus(other: Money) = Money(amount + other.amount)
operator fun times(ratio: Int) = Money(amount * ratio)
}
val total = Money(100) + Money(50) // Money(amount=150)
val doubled = Money(100) * 3 // Money(amount=300)3. 委托
// 属性委托:by lazy 懒加载(第一次访问才初始化,线程安全)
val expensive: String by lazy {
println("正在计算...")
"计算结果"
}
// 类委托:实现接口时委托给另一个对象
class MyList(private val inner: MutableList<String>) :
MutableList<String> by inner4. 类型别名与内联类
typealias Username = String
// 内联类(值类 value class):包装类型不产生装箱开销
@JvmInline
value class UserId(val value: Long)5. 注解与反射
annotation class MyAnnotation(val name: String = "default")
@MyAnnotation("hello")
fun test() {}
// 反射
fun printMethods(obj: Any) {
for (m in obj.javaClass.methods) {
println(m.name)
}
}九、与 Java 互操作
1. Kotlin 调用 Java
- 直接调用 Java 类、方法、静态成员
- Java 的可空性未知:返回类型被视为平台类型
String!,可用可空或非空类型接收
val date = java.util.Date() // 调用 Java 构造器
System.currentTimeMillis() // 调用静态方法
Math.abs(-1) // 静态方法2. Java 调用 Kotlin
- 顶层函数编译为
文件名Kt类的静态方法,用@file:JvmName改名 object单例可通过INSTANCE访问
// Java 中调用 Kotlin 顶层函数
TestKt.sayHello();3. 常用注解
| 注解 | 作用 |
|---|---|
@JvmName | 指定编译后的方法/文件名称 |
@JvmStatic | 把伴生对象方法编译为静态方法 |
@JvmField | 把属性暴露为 Java 字段(不生成 getter/setter) |
@JvmOverloads | 为默认参数生成多个重载方法 |
十、面试高频考点 ⭐
var和val的区别:可变 vs 只读引用;val不能重新赋值但对象内部状态可改变==与===的区别:结构相等(equals)vs 引用相等- 空安全的实现原理:
?编译期检查 + 平台类型 + 非空断言;?.编译为空判断,!!编译为checkNotNull - 作用域函数如何选择:
apply/also返回对象本身(配置/副作用),let/run/with返回结果(转换/计算) - 扩展函数为什么不能重写:编译成静态方法,调用取决于编译期静态类型
- data class 与普通类:自动生成 equals/hashCode/toString/copy/componentN
- 协程与线程:协程是轻量级线程,挂起不阻塞线程,一个线程可运行多个协程,基于 CPS 状态机实现
- inline 的作用:减少 Lambda 对象创建的开销,支持非局部返回
it的含义:Lambda 中只有一个参数时的隐式参数名- 智能转换 Smart Cast:不可变 val + 局部变量在判空后自动转换类型
总结
- Kotlin 是更现代、更安全、更简洁的 JVM 语言
- 学习路径:基础语法 → 空安全 → 面向对象 → 函数式 API → 协程 → 与 Java 互操作
- 建议在掌握 Java 的基础上,通过"对比思维"学习 Kotlin,事半功倍