一、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 = trueoverride 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: Intget() = 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.Delegatesclass 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
}
? ? ??