一、Kotlin 基础入门
1.1 Kotlin 简介
Kotlin 是一种由 JetBrains 开发的静态类型编程语言,运行在 Java 虚拟机上,也可以编译为 JavaScript 或原生代码。它于 2017 年被 Google 宣布为 Android 官方开发语言。
主要特点:
-
简洁:相比 Java 减少约 40% 的样板代码
-
安全:内置空安全机制
-
互操作:100% 兼容 Java
-
工具友好:由 JetBrains 开发,IDE 支持完善
1.2 开发环境搭建:Android Studio 配置
-
Android Studio 3.0+ 已内置 Kotlin 支持
-
可通过插件管理器确认 Kotlin 插件状态
-
转换现有 Java 文件:Code > Convert Java File to Kotlin File
1.3 基本语法结构
1.3.1 Hello World 程序
fun main() {
println("Hello, World!")
}
与 Java 对比:
-
没有类声明也能运行
-
函数使用
fun
关键字 -
分号可选
-
打印使用
println
而非System.out.println
1.4 变量与常量
1.4.1 变量声明
var mutableVar: String = "可变变量"
val immutableVal: Int = 42 // 类似 Java 的 final
类型推断:
var inferredString = "类型推断为 String"
val inferredInt = 123 // 推断为 Int
1.4.2 基本数据类型
Kotlin 中所有数据类型都是对象:
类型 | 位宽 | 示例 |
---|---|---|
Byte | 8 | 123 |
Short | 16 | 12345 |
Int | 32 | 1234567890 |
Long | 64 | 123L |
Float | 32 | 123.45f |
Double | 64 | 123.45 |
Char | 16 | 'A' |
Boolean | - | true/false |
类型转换:
val intValue = 42
val longValue = intValue.toLong() // 显式转换
1.4.3 字符串模板
val name = "Alice"
println("Hello, $name!") // 简单变量
println("Name length: ${name.length}") // 表达式
多行字符串:
val text = """
|第一行
|第二行
|第三行
""".trimMargin()
第二章:Kotlin 函数编程
2.1 函数定义
2.1.1 基本函数
fun sum(a: Int, b: Int): Int {
return a + b
}
2.1.2 表达式函数体
fun sum(a: Int, b: Int) = a + b
2.1.3 默认参数
fun greet(name: String = "World") {
println("Hello, $name!")
}
greet() // Hello, World!
greet("Alice") // Hello, Alice!
2.1.4 命名参数
fun createUser(
id: Int,
name: String,
email: String = "",
age: Int = 0
) { /*...*/ }
// 调用
createUser(
id = 1,
name = "Alice",
age = 25
)
2.2 高阶函数
2.2.1 函数作为参数
fun calculate(a: Int, b: Int, operation: (Int, Int) -> Int): Int {
return operation(a, b)
}
val result = calculate(10, 5) { x, y -> x + y }
2.2.2 函数作为返回值
fun getOperation(type: String): (Int, Int) -> Int {
return when(type) {
"add" -> { a, b -> a + b }
"multiply" -> { a, b -> a * b }
else -> { _, _ -> 0 }
}
}
2.3 Lambda 表达式
基本语法:
val sum = { x: Int, y: Int -> x + y }
println(sum(1, 2)) // 3
带接收者的 Lambda:
val stringPlus: String.(String) -> String = { this + it }
println("Hello".stringPlus("World")) // HelloWorld
2.4 内联函数
inline fun measureTime(block: () -> Unit) {
val start = System.currentTimeMillis()
block()
println("执行时间: ${System.currentTimeMillis() - start}ms")
}
measureTime {
// 要测量的代码
}
三、Kotlin 面向对象编程
3.1 类与对象:Kotlin的核心抽象单元
Kotlin的类系统在保持与Java互操作性的同时,通过精简的语法显著提升了开发效率。类的声明可以包含构造函数、属性初始化、成员函数等多个部分,但语法比Java更加紧凑。
3.1.1 主构造函数与初始化块
主构造函数是类头的一部分,它跟在类名后面,可以包含参数声明。这些参数不仅用于构造实例,还可以直接声明为类属性:
class Person(
val name: String, // 只读属性
var age: Int, // 可变属性
gender: String // 构造函数参数(非属性)
) {
init {
require(age > 0) { "年龄必须为正数" }
println("创建Person实例: $name")
}
val genderUpper = gender.uppercase() // 属性初始化
}
关键点:
-
init
块在对象创建时执行,可以有多个(按声明顺序执行) -
主构造函数参数加
val/var
才会成为属性 -
属性可以在类体内直接初始化
3.1.2 次构造函数与默认值
当需要多种构造方式时,可以定义次构造函数。但在Kotlin中,更推荐使用默认参数替代多个构造函数:
class User {
constructor(email: String) {
// 从email构造
}
constructor(facebookId: Long) {
// 从社交账号构造
}
}
// 更Kotlin化的方式(使用默认参数)
class BetterUser(
val email: String? = null,
val facebookId: Long? = null
) {
init {
require(email != null || facebookId != null) {
"至少需要一种登录方式"
}
}
}
3.2 继承体系:更安全的类扩展机制
Kotlin的继承系统通过明确的修饰符设计,强制开发者更谨慎地处理继承关系,避免了Java中常见的继承滥用问题。
3.2.1 类继承的基本规则
open class Animal(val name: String) { // 必须标记open才能被继承
open val sound = "..." // 必须标记open才能被重写
open fun makeSound() {
println(sound)
}
fun eat() { /* 默认final */ }
}
class Cat(name: String) : Animal(name) {
override val sound = "Meow" // 属性重写
final override fun makeSound() { // 标记final禁止进一步重写
super.makeSound()
println("追加猫特有的行为")
}
}
重要特性:
-
默认所有类都是final(与Java相反)
-
必须显式使用
open
允许继承 -
override
关键字强制要求显式标注 -
支持属性重写(而不仅是方法)
3.2.2 初始化顺序陷阱
Kotlin的类初始化顺序与Java不同,特别是在涉及属性重写时:
open class Base(val name: String) {
init {
println("初始化Base")
}
open val size: Int = name.length.also {
println("初始化Base.size")
}
}
class Derived(
name: String,
val lastName: String
) : Base(name.capitalize()) {
init {
println("初始化Derived")
}
override val size: Int = (super.size + lastName.length).also {
println("初始化Derived.size")
}
}
// 使用
Derived("hello", "world")
输出顺序:
-
Base的主构造函数参数求值(name.capitalize())
-
Base的init块
-
Base的属性初始化
-
Derived的主构造函数参数初始化
-
Derived的init块
-
Derived的重写属性初始化
3.3 接口进化:带有默认实现的契约
Kotlin接口可以包含抽象属性、抽象方法以及带有默认实现的方法,比Java 8的接口更灵活:
interface Clickable {
val clickable: Boolean // 抽象属性
fun click() // 抽象方法
fun showOff() = println("我是可点击的!") // 默认实现
}
interface Focusable {
fun showOff() = println("我是可聚焦的!")
}
class Button : Clickable, Focusable {
override val clickable: Boolean = true
override fun click() = println("按钮被点击")
// 必须解决接口冲突
override fun showOff() {
super<Clickable>.showOff()
super<Focusable>.showOff()
}
}
接口特性:
-
支持属性声明(但不能保存状态)
-
默认方法冲突必须显式解决
-
可以实现多个接口
-
与Java 8接口互操作
3.4 数据类:自动生成样板代码的利器
数据类(data class)是Kotlin中用于纯粹保存数据的特殊类,编译器会自动生成以下方法:
-
equals()
/hashCode()
-
toString()
-
copy()
-
解构声明(componentN()函数)
3.4.1 数据类的限制与技巧
data class Person(
val name: String,
val age: Int
) {
var address: String = "" // 不会参与equals/hashCode
// 自定义copy行为
fun copyWithPrefix(prefix: String) = copy(name = "$prefix$name")
}
// 使用示例
val person = Person("Alice", 25)
val (name, age) = person // 解构声明
val olderAlice = person.copy(age = 30)
重要限制:
-
主构造函数必须至少有一个参数
-
所有主构造参数必须标记为
val
或var
-
不能是
abstract
、open
、sealed
或inner
3.4.2 深度复制与嵌套数据类
对于包含可变引用或嵌套集合的数据类,需要注意浅复制问题:
data class Company(val name: String, val employees: MutableList<Employee>)
// 安全复制方式
val newCompany = company.copy(
employees = company.employees.toMutableList()
)
3.5 密封类:受限的类层次结构
密封类(sealed class)是枚举和抽象类的结合体,它限制了子类的可能类型,是实现代数数据类型(ADT)的理想选择。
3.5.1 密封类的典型应用
sealed class Result<out T> {
data class Success<T>(val data: T) : Result<T>()
data class Error(val exception: Throwable) : 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)
Result.Loading -> println("加载中...")
// 不需要else分支,所有情况已覆盖
}
优势:
-
编译时检查穷尽所有可能类型
-
保持多态特性(不同于枚举)
-
每个子类可以有多个实例(除非定义为object)
-
完美配合when表达式使用
3.5.2 密封类与枚举的选择
特性 | 密封类 | 枚举类 |
---|---|---|
实例数量 | 子类决定 | 固定数量 |
状态携带 | 可以携带复杂数据 | 通常无状态或简单状态 |
穷尽检查 | 支持 | 支持 |
多态性 | 保持 | 丢失 |
性能 | 稍低 | 更高 |
适用场景:
-
需要携带不同数据的相关类型 → 密封类
-
固定数量的简单常量 → 枚举
-
需要模式匹配的场景 → 密封类
3.6 对象声明与伴生对象:Kotlin的单例模式
Kotlin使用 object
关键字实现单例模式,比Java的实现更简洁安全。
3.6.1 对象声明的多种用途
// 单例对象
object DatabaseManager {
init {
println("数据库连接初始化")
}
fun query(sql: String) { /*...*/ }
}
// 伴生对象(类内部的静态成员)
class MyClass {
companion object Factory {
fun create(): MyClass = MyClass()
}
}
// 对象表达式(匿名对象)
val listener = object : MouseAdapter() {
override fun mouseClicked(e: MouseEvent) { /*...*/ }
}
关键点:
-
对象声明是延迟初始化的(首次访问时初始化)
-
伴生对象可以命名也可以匿名
-
伴生对象可以继承接口
-
JVM上伴生对象成员会生成静态字段(使用@JvmStatic注解)
3.6.2 伴生对象的进阶用法
interface JSONFactory<T> {
fun fromJSON(json: String): T
}
class Person(val name: String) {
companion object : JSONFactory<Person> {
override fun fromJSON(json: String): Person {
// 解析JSON...
return Person(/*...*/)
}
}
}
// 使用
val person = Person.fromJSON(jsonString)
四、Kotlin 高级特性
4.1 扩展函数与属性
扩展(Extensions)是Kotlin最强大的特性之一,它允许开发者为现有类添加新功能而无需继承或使用设计模式如装饰器。这种能力特别适合以下场景:
-
当你想为第三方库或SDK中的类添加功能时
-
需要保持代码整洁避免工具类泛滥时
-
希望创建领域特定语言(DSL)时
4.1.1 扩展函数
扩展函数实际上是静态方法的语法糖。编译后,扩展函数会被转换为一个静态方法,接收者对象作为第一个参数
fun String.addEnthusiasm(amount: Int = 1): String {
return this + "!".repeat(amount)
}
println("Hello".addEnthusiasm(3)) // Hello!!!
重要注意事项:
-
扩展函数是静态解析的,不支持运行时多态
-
如果扩展函数与成员函数签名相同,成员函数优先
-
扩展可以定义在顶层,也可以定义在类内部(作用域受限)
4.1.2 扩展属性
val String.numberOfVowels: Int
get() = count { it.toLowerCase() in setOf('a', 'e', 'i', 'o', 'u') }
println("Kotlin".numberOfVowels) // 2
4.2 运算符重载
Kotlin允许通过固定名称的函数重载数学运算符和比较操作,这使得自定义类型可以像基本类型一样参与运算,大幅提升代码可读性。
data class Point(val x: Int, val y: Int) {
operator fun plus(other: Point): Point {
return Point(x + other.x, y + other.y)
}
}
val p1 = Point(1, 2)
val p2 = Point(3, 4)
println(p1 + p2) // Point(x=4, y=6)
4.3 委托
委托(Delegation)是Kotlin实现组合优于继承原则的核心机制,通过by
关键字将接口实现委托给其他对象。
4.3.1 类委托的典型应用场景
-
装饰器模式:扩展功能而不修改原类
-
接口隔离:将大接口的实现拆分到不同对象
-
跨平台开发:将平台相关实现委托给具体平台模块
4.3.2类委托
interface Database {
fun save(data: String)
}
class RealDatabase : Database {
override fun save(data: String) { /*...*/ }
}
class DatabaseProxy(db: Database) : Database by db
4.3.3 属性委托
import kotlin.properties.Delegates
class User {
var name: String by Delegates.observable("<no name>") {
prop, old, new ->
println("$old -> $new")
}
}
val user = User()
user.name = "Alice" // 输出: <no name> -> Alice
user.name = "Bob" // 输出: Alice -> Bob
4.4 泛型
Kotlin的泛型系统在Java基础上进行了重要改进,解决了类型擦除带来的诸多限制。
4.4.1 型变注解深度解析
注解 | 位置 | 作用 | Java对应 |
---|---|---|---|
out | 类型参数 | 协变-只作为返回类型 | ? extends |
in | 类型参数 | 逆变-只作为参数类型 | ? super |
reified | 内联函数 | 保留类型信息 | 无 |
4.4.2 使用
class Box<T>(t: T) {
var value = t
}
// 使用
val intBox = Box<Int>(1)
val stringBox = Box("String") // 类型推断
五、Kotlin 协程
5.1 协程基础
5.1.1 基本概念
协程(Coroutine)本质上是可挂起的计算实例,具有以下核心特点:
-
挂起不阻塞:挂起函数会释放底层线程供其他协程使用
-
结构化并发:协程之间存在明确的父子关系
-
调度灵活:可在不同线程间自由切换
-
资源高效:单个线程可运行数千个协程
5.1.2 协程与线程对比
特性 | 线程 | 协程 |
---|---|---|
创建开销 | 1MB+ 内存 | 几十字节 |
切换成本 | 微秒级 | 纳秒级 |
并发数量 | 数百到数千 | 数百万 |
阻塞影响 | 阻塞整个线程 | 仅挂起当前协程 |
通信机制 | 共享内存+锁 | Channel/Flow |
5.1.3 基本使用
import kotlinx.coroutines.*
fun main() = runBlocking {
launch {
delay(1000L)
println("World!")
}
println("Hello,")
}
5.2 协程构建器
// launch: 启动一个不返回结果的协程
val job = launch { /*...*/ }
// async: 启动一个返回 Deferred 结果的协程
val deferred = async {
delay(1000L)
"Result"
}
println(deferred.await())
// runBlocking: 阻塞当前线程直到协程完成
runBlocking {
delay(1000L)
}
5.3 协程上下文与调度器
Dispatchers.Default // CPU 密集型工作
Dispatchers.IO // IO 密集型工作
Dispatchers.Main // UI 线程(Android)
Dispatchers.Unconfined // 不限制在任何特定线程
// 使用示例
launch(Dispatchers.IO) {
// 网络请求或文件操作
}
5.4 协程取消
val job = launch {
repeat(1000) { i ->
ensureActive() // 检查是否取消
println("job: I'm sleeping $i ...")
delay(500L)
}
}
delay(1300L)
job.cancelAndJoin()
六、Kotlin 最佳实践
6.1 代码风格
-
命名约定:
-
类/对象:PascalCase
-
函数/变量:camelCase
-
常量:UPPER_SNAKE_CASE
-
-
格式化:
-
4空格缩进
-
类/函数之间空一行
-
参数列表过长时换行
-
6.2 性能优化
-
内联小的高阶函数
-
避免不必要的对象创建
-
使用序列(Sequence)处理大数据集
-
注意协程的调度器选择
6.3 常见陷阱
-
空安全:
val list: List<String> = emptyList() val firstItem = list.firstOrNull() // 安全
-
相等性:
==
对应equals(),
===
检查引用相等 - 初始化顺序:
class Example {
val a = b + 1 // 错误:b 还未初始化
val b = 2
}