例外處理#

例外(Exception)是程式執行時發生的錯誤事件。Kotlin 使用 trycatchfinally 來捕捉和處理例外,避免程式意外崩潰。


1. 基本語法#

1
2
3
4
5
6
7
try {
    // 可能拋出例外的程式碼
} catch (e: 例外型別) {
    // 例外處理邏輯
} finally {
    // 無論是否發生例外都會執行(可選)
}

2. 捕捉例外#

範例:除以零#

1
2
3
4
5
6
7
8
9
fun main() {
    try {
        val result = 10 / 0
        println(result)
    } catch (e: ArithmeticException) {
        println("發生算術錯誤:${e.message}")
    }
    // 輸出:發生算術錯誤:/ by zero
}

範例:字串轉數字失敗#

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
fun main() {
    val input = "abc"
    try {
        val number = input.toInt()
        println("數字是:$number")
    } catch (e: NumberFormatException) {
        println("無法將 \"$input\" 轉換為數字")
    }
    // 輸出:無法將 "abc" 轉換為數字
}

3. finally 區塊#

finally 區塊無論例外是否發生都會執行,常用於釋放資源。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
fun readFile(filename: String) {
    println("開啟檔案:$filename")
    try {
        // 模擬可能發生的錯誤
        if (filename.isEmpty()) throw IllegalArgumentException("檔名不能為空")
        println("讀取成功")
    } catch (e: IllegalArgumentException) {
        println("錯誤:${e.message}")
    } finally {
        println("關閉檔案") // 一定會執行
    }
}

fun main() {
    readFile("data.txt")
    println("---")
    readFile("")
    // 輸出:
    // 開啟檔案:data.txt
    // 讀取成功
    // 關閉檔案
    // ---
    // 開啟檔案:
    // 錯誤:檔名不能為空
    // 關閉檔案
}

4. 捕捉多種例外#

可以使用多個 catch 區塊分別處理不同類型的例外。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
fun parse(input: String) {
    try {
        val number = input.toInt()
        val result = 100 / number
        println("結果:$result")
    } catch (e: NumberFormatException) {
        println("格式錯誤:\"$input\" 不是有效數字")
    } catch (e: ArithmeticException) {
        println("算術錯誤:不能除以零")
    }
}

fun main() {
    parse("abc")  // 輸出:格式錯誤:"abc" 不是有效數字
    parse("0")    // 輸出:算術錯誤:不能除以零
    parse("5")    // 輸出:結果:20
}

5. throw:主動拋出例外#

使用 throw 可以主動拋出例外。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
fun checkAge(age: Int) {
    if (age < 0) throw IllegalArgumentException("年齡不能為負數:$age")
    if (age > 150) throw IllegalArgumentException("年齡超出合理範圍:$age")
    println("年齡有效:$age")
}

fun main() {
    try {
        checkAge(25)
        checkAge(-5)
    } catch (e: IllegalArgumentException) {
        println("輸入錯誤:${e.message}")
    }
    // 輸出:
    // 年齡有效:25
    // 輸入錯誤:年齡不能為負數:-5
}

6. try 作為表達式#

Kotlin 的 try 可以作為表達式,直接回傳值。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
fun toIntOrDefault(input: String, default: Int = 0): Int {
    return try {
        input.toInt()
    } catch (e: NumberFormatException) {
        default
    }
}

fun main() {
    println(toIntOrDefault("42"))        // 輸出:42
    println(toIntOrDefault("abc"))       // 輸出:0
    println(toIntOrDefault("abc", -1))   // 輸出:-1
}

7. 自訂例外#

繼承 Exception 類別可以建立自訂例外,提供更具語意的錯誤訊息。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class InsufficientFundsException(amount: Double) :
    Exception("餘額不足,嘗試提領 $amount 元")

class BankAccount(private var balance: Double) {
    fun withdraw(amount: Double) {
        if (amount > balance) throw InsufficientFundsException(amount)
        balance -= amount
        println("提領成功,剩餘餘額:$balance 元")
    }
}

fun main() {
    val account = BankAccount(1000.0)
    try {
        account.withdraw(500.0)
        account.withdraw(800.0)
    } catch (e: InsufficientFundsException) {
        println("交易失敗:${e.message}")
    }
    // 輸出:
    // 提領成功,剩餘餘額:500.0 元
    // 交易失敗:餘額不足,嘗試提領 800.0 元
}

8. 常見例外型別#

例外型別常見原因
ArithmeticException除以零
NumberFormatException字串無法轉換為數字
NullPointerException對 null 物件呼叫方法
IndexOutOfBoundsException陣列或集合索引超出範圍
IllegalArgumentException傳入不合法的參數
IllegalStateException物件狀態不允許執行此操作
ClassCastException型別轉換失敗

9. Kotlin 沒有受檢例外#

Java 有 受檢例外(Checked Exception),強制要求呼叫方處理或宣告例外。Kotlin 沒有 受檢例外,所有例外都是非受檢的,讓程式碼更簡潔,但也需要開發者自行確保重要例外有被處理。


Reference#

https://kotlinlang.org/docs/exceptions.html