## **[異常](http://www.hmoore.net/alex_wsc/android_kotlin/1318345)處理**
[TOC]
### **異常概述**
什么是異常?說到異常處理,我們想到了try、catch、finally 這樣的3個關鍵字,是的,Kotlin里面也是這樣的3個關鍵字,意義和Java也是一樣的。我們對可能出現異常的語句使用try語句塊包裹,如果try里面的語句真的出現了異常,代碼將會跳轉到catch語句塊里面。無論代碼執行順序如何,最終都會走到finally語句塊中。
Kotlin 中所有異常類都是 Throwable 類的子孫類。 每個異常都有消息、堆棧回溯信息和可選的原因。
示例
```
fun divide(a: Int, b: Int): Int {
var result: Int = a / b //定義一個result變量用于存放a/b的值
return result //將結果返回
}
fun main(args: Array<String>) {
var result = divide(5, 0) //調用divide()方法
println(result)
}
```
運行結果
```
Exception in thread "main" java.lang.ArithmeticException: / by zero
at FileKt.divide (File.kt:2)
at FileKt.main (File.kt:7)
```
從運行結果可知,程序在運行過程中拋出一個算術異常(ArithmeticException),這個異常是因為程序的第6行代碼中,調用divide()方法時第2個參數傳入了0,當程序執行第3行代碼時出現了被0除的錯誤。出現這個異常之后,程序會立即結束,不會再執行下面的其他代碼。
ArithmeticException異常類只是Kotlin異常類中的一種,在Kotlin中還提供了其他異常類,如ClassNotFoundException、ArrayIndexOutOfBoundsException、IllegalArgumentException等,這些類都繼承自java.lang.Throwable類。
要拋出異常可以使用 throw -表達式來拋出異常。
```
throw MyException("Hi There!")
```
要捕獲異常,可以使用try表達式
```
try {
// 一些代碼
}
catch (e: SomeException) {
// 處理程序
}
finally {
// 可選的 finally 塊
}
```
>[info]注意:
①、try表達式可以有0到多個 catch 塊。 finally 塊可以省略。 但是 catch 和 finally 塊至少應該存在一個。
②、在Kotlin中, throw和try都是表達式,這意味著它們可以被賦值給一個變量。這個在處理一些邊界問題的時候確實非常有用
### try…catch和finally
Kotlin中提供了一種對異常進行處理的方式——**異常捕獲**。異常捕獲通常使用try…catch語句來實現,異常捕獲的語法格式如下:
```
try {
// 程序代碼
}catch (e: SomeException) { // e: 后面的這個類可以是Exception 類或者其子類
// 對捕獲的Exception 進行處理
}
```
上述語法格式中,try代碼塊中編寫的是可能發生異常的Kotlin語句,catch代碼塊中編寫的是對捕獲的異常進行處理的代碼。當try代碼塊中的程序發生了異常,系統會將這個異常的信息封裝成一個異常對象,并將這個對象傳遞給catch代碼塊。catch代碼塊中傳遞的Exception類型的參數是指定這個程序能夠接收的異常類型,這個參數的類型必須是Exception類或者其子類。
* **示例**
```
fun divide(a: Int, b: Int): Int {
var result: Int = a / b //定義一個result變量用于存放a/b的值
return result //將結果返回
}
fun main(args: Array<String>) {
try {
var result: Int = divide(5, 0)
println(result)
} catch (e: Exception) {
println("捕獲的異常信息為:" + e.message)
}
println("程序繼續執行")
}
```
運行結果
```
捕獲的異常信息為:/ by zero
程序繼續執行
```
在上述代碼中,對可能發生異常的代碼用try…catch語句進行了處理,在try代碼塊中發生被0除異常之后,程序會接著執行catch中的代碼,通過調用Exception對象中的message來返回異常信息“by/zero”,catch代碼塊對異常處理完畢之后,程序會繼續向下執行,而不會出現異常終止。
>[info]需要注意的是,在try代碼塊中,發生異常的語句后面的代碼是不會被執行的,如本案例中的第8行代碼的輸出語句就沒有執行。
在程序中,由于功能的需求,有些語句在無論程序是否發生異常的情況下都要執行,這時就可以在try…catch語句后添加一個finally代碼塊。接下來我們來對上面的代碼進行修改,看一下finally代碼塊的用法,具體代碼如下所示。
```
fun divide(a: Int, b: Int): Int {
var result: Int = a / b //定義一個result變量用于存放a/b的值
return result //將結果返回
}
fun main(args: Array<String>) {
try {
var result: Int = divide(5, 0) //調用divide()方法
println(result)
} catch (e: Exception) {
println("捕獲的異常信息為:" + e.message)
return //用于結束當前語句
} finally {
println("進入finally代碼塊")
}
println("程序繼續執行")
}
```
運行結果
```
捕獲的異常信息為:/ by zero
進入finally代碼塊
```
在上述代碼中,catch代碼塊中增加了一個return語句,用于結束當前方法,此時第15行的代碼`println("程序繼續執行")`就不會執行了,而finally中的代碼仍會執行,并不會被return語句所影響。
不論程序發生異常還是用return語句結束當前方法,finally中的語句都會執行,由于finally語句的這種特殊性,因此在程序設計時,經常會在try…catch后使用finally代碼塊來完成必須做的事情,如釋放系統資源。
>[info]需要注意的是,當catch代碼塊中執行了System.exit(0)語句時,finally中的代碼塊就不會執行。System.exit(0)表示退出當前的程序,退出當前程序后,任何代碼都不能再執行了。
#### 與Java中的try…catch…finally對比
與Java不同的是,**在try…catch…finally中,try是一個表達式,即它可以返回一個值,try表達式的返回值是try代碼塊中的最后一個表達式的結果或者是所有catch代碼塊中的最后一個表達式的結果,finally代碼塊中的內容不會影響表達式的結果**。接下來我們通過一個案例來演示try…catch…finally代碼塊中的結果,代碼如下所示
```
fun main(args: Array<String>) {
var age: Int? = try {
10 //正常運行,返回10
} catch (e: NumberFormatException) {
12
null
} finally {
13 //finally代碼塊不會影響表達式結果
}
var score: Int? = try {
Integer.parseInt("s" + 1)
} catch (e: NumberFormatException) {
60
null //捕獲異常,返回null
} finally {
70 //finally代碼塊不會影響表達式結果
}
println("年齡age=${age}")
println("分數score=${score}")
}
```
運行結果
```
年齡age=10
分數score=null
```
上述代碼中,分別定義了2個變量age與score,age表示年齡,score表示分數,這2個變量的值都是用try…catch…finally代碼塊包括起來的,根據程序的運行結果可知,當給變量age賦值時,程序正常運行,則age的值是10,即try表達式的結果。當給變量score賦值時,程序出現了異常(s不知從何而來,Integer是Java中的類,字符串默認值是null,即為空未分配對象,看Integer源碼就知道,Integer.parseInt方法參數為null時,`throw new NumberFormatException("null")`),此時score的值是null,即catch代碼塊中最后一個表達式的結果。其中finally代碼塊中的內容不影響try和catch代碼塊中表達式的結果。
總結:
在try…catch…finally語句中,catch代碼塊可以有多個也可以沒有,finally代碼塊可以省略,但是catch和finally代碼塊至少應有一個是存在的。
#### **Try 是?個表達式,可以有一個返回值**
【Kotlin相比于Java,可以使用變量try表達式返回值】。try表達式要么有try語句塊的最后一行決定,要么由catch語句塊的最后一行決定;finally代碼段的內容不會影響try表達式的結果值。參考如下代碼:

針對以上代碼,如果try語句塊沒有異常,返回的就是字符串轉換轉換的結果,如果出現異常,就會走到catch語句塊,返回的就是0。
運行結果:

### **throw表達式**
前面的代碼中,由于調用的是自己寫的divide()方法,因此很清楚該方法可能會發生異常。試想一下,如果去調用一個別人寫的方法時,是否能知道別人寫的方法是否會有異常呢?這是很難做出判斷的。針對這種情況,在Kotlin中,允許在可能發生異常的代碼中通過throw關鍵字對外聲明該段代碼可能會發生異常,這樣在使用這段代碼時,就明確地知道該段代碼有異常,并且必須對異常進行處理。
throw關鍵字拋出異常的語法格式如下:
```
throw ExceptionType("異常信息")
```
上述語法格式是通過throw表達式來拋出一個異常,這個表達式需要放在容易出現異常的代碼中,也可以作為Elvis表達式(三元表達式)的一部分,throw后面需要聲明發生異常的類型,通常將這種做法稱為拋出一個異常。接下來我們來修改前面的代碼,在調用divide()方法的代碼上方拋出一個異常,修改后的代碼如下所示
```
fun divide(a: Int, b: Int): Int {
if (b==0)throw ArithmeticException("發生異常")
var result: Int = a / b
return result
}
fun main(args: Array<String>) {
var result: Int = divide(5, 0) //調用divide()方法
println(result)
}
```
報錯如下
```
Exception in thread "main" java.lang.ArithmeticException: 發生異常
at FileKt.divide (File.kt:2)
at FileKt.main (File.kt:7)
```
上述代碼中,第7行代碼調用divide()方法時傳遞的第2個參數是0,程序在運行時不會發生被0除的異常,由于在divide()方法中拋出了一個算術異常ArithmeticException,因此在調用divide()方法時就必須對拋出的異常進行處理,否則就會發生編譯錯誤。接下來我們對代碼進行修改,在調用divide()方法時對其進行try…catch處理,修改后的代碼如下所示。
```
fun divide(a: Int, b: Int): Int {
if (b==0)throw ArithmeticException("發生異常")
var result: Int = a / b
return result
}
fun main(args: Array<String>) {
try {
var result: Int = divide (5, 0) //調用divide()方法
println(result)
} catch (e: ArithmeticException) {
println(e.message)
}
}
```
運行結果
```
發生異常
```
上述代碼中,在main()函數中通過try…catch語句已經捕獲了divide()方法中拋出的異常,由于該程序的divide()方法中傳遞的變量b的值為0,因此運行結果為發生異常。如果將變量b的值設置為1,則運行結果為5。
#### 用于Elvis 表達式的?部分
在 Kotlin 中 throw 是表達式,所以你可以使?它(?如)作為 Elvis 表達式的?部分:
```
val s = person.name ?: throw IllegalArgumentException("Name required")
```
#### Nothing類型
參考后面的章節——[kotlin.Nothing類型](http://www.hmoore.net/alex_wsc/android_kotlin/1053641)和官方文檔[Nothing 類型](http://www.kotlincn.net/docs/reference/exceptions.html#nothing-%E7%B1%BB%E5%9E%8B)
在 Kotlin 中 throw 是表達式,它的類型是特殊類型 Nothing。 該類型沒有值。跟C、Java中的 void 意思一樣,?是?于標記永遠不能達到的代碼位置。
在Kotlin中,有些函數的“返回值類型”的概念沒有任何意義,此時Kotlin使用一種特殊的返回值類型Nothing來表示,Nothing是一個空類型(uninhabited type),也就是說在程序運行時不會出任何一個Nothing類型的對象。在程序中,可以使用Nothing類型來標記一個永遠不會有返回數據的函數,
我們在代碼中,用 Nothing 來標記無返回的函數:
~~~
fun main(args: Array<String>) {
fail("Error occurred")
}
fun fail(message:String):Nothing{
throw IllegalStateException(message)
}
~~~
當調用含有Nothing類型的函數時,可能會遇到這個類型的一種情況就是類型推斷,這個類型的可空類型為Nothing?,該類型有一個可能的值是null,如果用null來初始化一個變量的值,并且又不能確定該變量的具體類型時,編譯器會推斷這個變量的類型為Nothing?類型,具體示例代碼如下:
```
val x = null // 變量“x”的類型為“Nothing? ”
val l = listOf(null) // 變量“l”的類型為“List<Nothing?>”
```
上述代碼中,變量x的初始值為null,并且該變量的具體類型不確定,因此編譯器會推斷該變量的類型為“Nothing?”。變量l的初始值為listOf(null),可以看到List集合的初始值為null,此時不能確定該集合的具體類型,因此編譯器會推斷該集合的類型為“List<Nothing?>”。
* 另外,如果把一個throw表達式的值賦值給一個變量,需要顯式聲明類型為 Nothing ,
```
val ex:Nothing = throw Exception("YYYYYYYY")
```
>[info]【注意】另外,因為ex變量是Nothing類型,沒有任何值,所以無法當做參數傳給函數
可能會遇到這個類型的另?種情況是類型推斷。這個類型的可空變體 Nothing? 有?個可能的值是 null 。如果? null 來初始化?個要推斷類型的值,??沒有其他信息可?于確定更具體的類型時,編譯器會推斷出 Nothing? 類型:
```
val x = null // “x”具有類型 `Nothing?`
val l = listOf(null) // “l”具有類型 `List<Nothing?>
```
### **沒有受檢異常**
**Kotlin沒有受檢異常(Checked Exceptions)**。
Java里面有兩種異常類型,一種是受檢異常(checked exception),一種是非受檢異常(uncheckedexception)。
之所以編寫Java代碼的時候,IDE會提示進行try catch操作,因為編譯時編譯器會檢查受檢異常。
受檢異常(Checked Exceptions)顯得比較麻煩,一直以來爭議比較大,可能會導致java API變得復雜,程序員編寫代碼的時候需要進行大量的try catch操作。所以,【Kotlin相比于Java,沒有了受檢異常,IDE不會提示進行try catch操作】。
寫代碼的時候,IDE調用某一個方法,這個方法即使可能拋出異常,IDE也不會提示你進行`try…catch`操作,直接運行該程序時會拋出異常信息。參考如下代碼:

針對以上代碼,如果是java代碼,【Integer.parseInt(line)】,這樣的代碼是會提示我們進行trycatch操作的,但是Kotlin不會提示。如果直接運行會拋出轉換異常,參考截圖:

我們可以自己給它加上try catch操作,參考如下代碼:

* **JDK舉例**
以下是 JDK 中 StringBuilder 類實現的?個?例接?:
```
Appendable append(CharSequence csq) throws IOException;
```
這個簽名是什么意思? 它是說,每次我追加?個字符串到?些東西(?個 StringBuilder 、某種?志、?個控制臺等)上時我就必須捕獲那些IOException 。 為什么?因為它可能正在執? IO 操作( Writer 也實現了 Appendable )…… 所以它導致這種代碼隨處可?的出現:
```
try {
log.append(message)
}
catch (IOException e) {
// 必須要安全
}
```
**結論**:通過一些小程序測試得出的結論是異常規范會同時提高開發者的生產力與代碼質量,但是大型軟件項目的經驗表明一個不同的結論——生產力降低、代碼質量很少或沒有提高。
### 自定義異常
在Kotlin標準庫中封裝了大量的異常類,雖然這些異常類可以描述編程時出現的大部分異常情況,但是在程序開發中有時可能需要描述程序中特有的異常情況,例如在設計一個變量name的值時,不允許該變量的值為null,為了解決這個問題,Kotlin與Java一樣,也允許用戶自定義異常,但**自定義的異常類必須繼承自Throwable類或其子類**,自定義異常類的主構函數可以傳遞一個String類型的message參數,也可以不傳遞參數,自定義異常類的語法格式如下:
```
//主構函數帶參數的異常類
class異常類名稱(override val message: String?) : Throwable() {}
//主構函數不帶參數的異常類
class異常類名稱() : Throwable() {}
```
在上述語法格式中,主構造函數帶message參數的異常類,在拋出異常信息時會打印message信息,主構造函數不帶message參數的異常類,在拋出異常信息時不會打印異常的具體信息。
示例
```
class MyException(override val message: String?) : Throwable() {
}
fun main(args: Array<String>) {
var name: String? = null
name?.length ?: throw MyException("變量name的值為null")
println("name.length=${name.length}")
}
```
報錯如下
```
Exception in thread "main" MyException: 變量name的值為null
at FileKt.main (File.kt:5)
```
從運行結果可以看出,程序在運行時發生了異常,這是因為在程序中使用throw關鍵字拋出異常對象時,需要使用`try…catch`語句對拋出的異常進行處理。為了解決該程序運行時發生異常的問題,可以修改該程序,在拋出異常的地方使用`try…catch`語句對異常進行處理,修改后的代碼如下所示。
```
class MyException(override val message: String?) : Throwable() {
}
fun main(args: Array<String>) {
var name: String? = null
try {
name?.length ?: throw MyException("變量name的值為null")
println("name.length=${name.length}")
} catch (e: MyException) {
println(e.message)
}
}
```
運行結果
```
變量name的值為null
```
在上述代碼中,使用了一個try…catch語句用于捕獲變量name的值為null時拋出的異常,在調用“name?.length”代碼時,由于變量name的值不能為null,程序會拋出一個自定義異常MyException,該異常被捕獲后最終被catch代碼塊處理,并打印異常信息。
- 前言
- Kotlin簡介
- IntelliJ IDEA技巧總結
- idea設置類注釋和方法注釋模板
- 像Android Studion一樣創建工程
- Gradle
- Gradle入門
- Gradle進階
- 使用Gradle創建一個Kotlin工程
- 環境搭建
- Androidstudio平臺搭建
- Eclipse的Kotlin環境配置
- 使用IntelliJ IDEA
- Kotlin學習路線
- Kotlin官方中文版文檔教程
- 概述
- kotlin用于服務器端開發
- kotlin用于Android開發
- kotlin用于JavaScript開發
- kotlin用于原生開發
- Kotlin 用于數據科學
- 協程
- 多平臺
- 新特性
- 1.1的新特性
- 1.2的新特性
- 1.3的新特性
- 開始
- 基本語法
- 習慣用法
- 編碼規范
- 基礎
- 基本類型
- 包與導入
- 控制流
- 返回與跳轉
- 類與對象
- 類與繼承
- 屬性與字段
- 接口
- 可見性修飾符
- 擴展
- 數據類
- 密封類
- 泛型
- 嵌套類
- 枚舉類
- 對象
- 類型別名
- 內嵌類
- 委托
- 委托屬性
- 函數與Lambda表達式
- 函數
- Lambda表達式
- 內聯函數
- 集合
- 集合概述
- 構造集合
- 迭代器
- 區間與數列
- 序列
- 操作概述
- 轉換
- 過濾
- 加減操作符
- 分組
- 取集合的一部分
- 取單個元素
- 排序
- 聚合操作
- 集合寫操作
- List相關操作
- Set相關操作
- Map相關操作
- 多平臺程序設計
- 平臺相關聲明
- 以Gradle創建
- 更多語言結構
- 解構聲明
- 類型檢測與轉換
- This表達式
- 相等性
- 操作符重載
- 空安全
- 異常
- 注解
- 反射
- 作用域函數
- 類型安全的構造器
- Opt-in Requirements
- 核心庫
- 標準庫
- kotlin.test
- 參考
- 關鍵字與操作符
- 語法
- 編碼風格約定
- Java互操作
- Kotlin中調用Java
- Java中調用Kotlin
- JavaScript
- 動態類型
- kotlin中調用JavaScript
- JavaScript中調用kotlin
- JavaScript模塊
- JavaScript反射
- JavaScript DCE
- 原生
- 并發
- 不可變性
- kotlin庫
- 平臺庫
- 與C語言互操作
- 與Object-C及Swift互操作
- CocoaPods集成
- Gradle插件
- 調試
- FAQ
- 協程
- 協程指南
- 基礎
- 取消與超時
- 組合掛起函數
- 協程上下文與調度器
- 異步流
- 通道
- 異常處理與監督
- 共享的可變狀態與并發
- Select表達式(實驗性)
- 工具
- 編寫kotlin代碼文檔
- 使用Kapt
- 使用Gradle
- 使用Maven
- 使用Ant
- Kotlin與OSGI
- 編譯器插件
- 編碼規范
- 演進
- kotlin語言演進
- 不同組件的穩定性
- kotlin1.3的兼容性指南
- 常見問題
- FAQ
- 與Java比較
- 與Scala比較(官方已刪除)
- Google開發者官網簡介
- Kotlin and Android
- Get Started with Kotlin on Android
- Kotlin on Android FAQ
- Android KTX
- Resources to Learn Kotlin
- Kotlin樣品
- Kotlin零基礎到進階
- 第一階段興趣入門
- kotlin簡介和學習方法
- 數據類型和類型系統
- 入門
- 分類
- val和var
- 二進制基礎
- 基礎
- 基本語法
- 包
- 示例
- 編碼規范
- 代碼注釋
- 異常
- 根類型“Any”
- Any? 可空類型
- 可空性的實現原理
- kotlin.Unit類型
- kotlin.Nothing類型
- 基本數據類型
- 數值類型
- 布爾類型
- 字符型
- 位運算符
- 變量和常量
- 語法和運算符
- 關鍵字
- 硬關鍵字
- 軟關鍵字
- 修飾符關鍵字
- 特殊標識符
- 操作符和特殊符號
- 算術運算符
- 賦值運算符
- 比較運算符
- 邏輯運算符
- this關鍵字
- super關鍵字
- 操作符重載
- 一元操作符
- 二元操作符
- 字符串
- 字符串介紹和屬性
- 字符串常見方法操作
- 字符串模板
- 數組
- 數組介紹創建及遍歷
- 數組常見方法和屬性
- 數組變化以及下標越界問題
- 原生數組類型
- 區間
- 正向區間
- 逆向區間
- 步長
- 類型檢測與類型轉換
- is、!is、as、as-運算符
- 空安全
- 可空類型變量
- 安全調用符
- 非空斷言
- Elvis操作符
- 可空性深入
- 可空性和Java
- 函數
- 函數式編程概述
- OOP和FOP
- 函數式編程基本特性
- 組合與范疇
- 在Kotlin中使用函數式編程
- 函數入門
- 函數作用域
- 函數加強
- 命名參數
- 默認參數
- 可變參數
- 表達式函數體
- 頂層、嵌套、中綴函數
- 尾遞歸函數優化
- 函數重載
- 控制流
- if表達式
- when表達式
- for循環
- while循環
- 循環中的 Break 與 continue
- return返回
- 標簽處返回
- 集合
- list集合
- list集合介紹和操作
- list常見方法和屬性
- list集合變化和下標越界
- set集合
- set集合介紹和常見操作
- set集合常見方法和屬性
- set集合變換和下標越界
- map集合
- map集合介紹和常見操作
- map集合常見方法和屬性
- map集合變換
- 集合的函數式API
- map函數
- filter函數
- “ all ”“ any ”“ count ”和“ find ”:對集合應用判斷式
- 別樣的求和方式:sumBy、sum、fold、reduce
- 根據人的性別進行分組:groupBy
- 扁平化——處理嵌套集合:flatMap、flatten
- 惰性集合操作:序列
- 區間、數組、集合之間轉換
- 面向對象
- 面向對象-封裝
- 類的創建及屬性方法訪問
- 類屬性和字段
- 構造器
- 嵌套類(內部類)
- 枚舉類
- 枚舉類遍歷&枚舉常量常用屬性
- 數據類
- 密封類
- 印章類(密封類)
- 面向對象-繼承
- 類的繼承
- 面向對象-多態
- 抽象類
- 接口
- 接口和抽象類的區別
- 面向對象-深入
- 擴展
- 擴展:為別的類添加方法、屬性
- Android中的擴展應用
- 優化Snackbar
- 用擴展函數封裝Utils
- 解決煩人的findViewById
- 擴展不是萬能的
- 調度方式對擴展函數的影響
- 被濫用的擴展函數
- 委托
- 委托類
- 委托屬性
- Kotlin5大內置委托
- Kotlin-Object關鍵字
- 單例模式
- 匿名類對象
- 伴生對象
- 作用域函數
- let函數
- run函數
- with函數
- apply函數
- also函數
- 標準庫函數
- takeIf 與 takeUnless
- 第二階段重點深入
- Lambda編程
- Lambda成員引用高階函數
- 高階函數
- 內聯函數
- 泛型
- 泛型的分類
- 泛型約束
- 子類和子類型
- 協變與逆變
- 泛型擦除與實化類型
- 泛型類型參數
- 泛型的背后:類型擦除
- Java為什么無法聲明一個泛型數組
- 向后兼容的罪
- 類型擦除的矛盾
- 使用內聯函數獲取泛型
- 打破泛型不變
- 一個支持協變的List
- 一個支持逆變的Comparator
- 協變和逆變
- 第三階段難點突破
- 注解和反射
- 聲明并應用注解
- DSL
- 協程
- 協程簡介
- 協程的基本操作
- 協程取消
- 管道
- 慕課霍丙乾協程筆記
- Kotlin與Java互操作
- 在Kotlin中調用Java
- 在Java中調用Kotlin
- Kotlin與Java中的操作對比
- 第四階段專題練習
- 朱凱Kotlin知識點總結
- Kotlin 基礎
- Kotlin 的變量、函數和類型
- Kotlin 里那些「不是那么寫的」
- Kotlin 里那些「更方便的」
- Kotlin 進階
- Kotlin 的泛型
- Kotlin 的高階函數、匿名函數和 Lambda 表達式
- Kotlin協程
- 初識
- 進階
- 深入
- Kotlin 擴展
- 會寫「18.dp」只是個入門——Kotlin 的擴展函數和擴展屬性(Extension Functions / Properties)
- Kotlin實戰-開發Android