Kotlin 型別檢查與轉型 (Type Checks and Casts)
Kotlin 的型別系統非常聰明,最著名的特性就是 Smart Casts (智慧轉型)。 這讓你不需要像 Java 那樣寫一堆重複的轉型程式碼。
is 與 !is 運算子
用來檢查一個物件是否屬於某個型別 (相當於 Java 的 instanceof)。
if (obj is String) {
println(obj.length)
}
if (obj !is String) { // 不是 String
println("Not a String")
}
Smart Casts (智慧轉型)
當你用 is 檢查過型別後,編譯器會自動把變數當作該型別來處理!
你不需要再寫 (String) obj 這種轉型程式碼。
fun demo(x: Any) {
if (x is String) {
// x 自動被轉型成 String,可以直接呼叫 .length
println(x.length)
}
}
邏輯運算中的 Smart Casts
Smart Casts 甚至在 && 和 || 的右側也有效:
// 如果 x 不是 String,|| 後面就不會執行,所以右邊 x 一定是 String
if (x !is String || x.length == 0) return
// 如果 x is String 為真,&& 右邊 x 就會被視為 String
if (x is String && x.length > 0) {
println(x.length)
}
顯式轉型 (Explicit Casts)
如果你確定某個變數是特定型別,可以使用 as 運算子強轉。
Unsafe Cast (as)
如果轉型失敗,會拋出 ClassCastException。
val x: Any = "Hello"
val s: String = x as String // OK
val i: Int = x as Int // Exception!
Safe Cast (as?)
推薦使用!如果轉型失敗,會回傳 null,而不會拋出例外。
val x: Any = "Hello"
val i: Int? = x as? Int // 回傳 null
println(i) // null
只有 Unsafe Cast 才會拋出例外
如果是 as? (Safe Cast),轉型失敗只會回傳 null,所以為了程式的穩定性,強烈建議優先使用 as?。
泛型轉型的陷阱 (Unchecked Casts)
由於 Java/Kotlin 的泛型在執行時期會被 擦除 (Type Erasure),所以你無法在 runtime 檢查泛型的具體型別。
val list: List<Any> = listOf(1, 2, 3)
// 警告:Unchecked cast: List<Any> to List<String>
// 這行程式碼在編譯時只會警告,執行時會成功 (因為 runtime 只知道它是 List)
val strings = list as List<String>
// 但當你試圖存取元素時,就會爆掉!
// println(strings[0].length) // ClassCastException: Integer cannot be cast to String
要解決這個問題,通常需要使用 reified type parameters (inline function),這會在之後的 泛型 章節介紹。
Type Aliases (型別別名)
如果你的型別名稱很長,或是由多個泛型組成,可以使用 typealias 取個好讀的別名。
// 為複雜的 Map 型別取別名
typealias UserMap = Map<String, List<String>>
// 為函數型別取別名
typealias ClickHandler = (Int, String) -> Unit
fun main() {
val users: UserMap = mapOf()
val handler: ClickHandler = { id, name -> println("$id: $name") }
}
typealias不會產生新的類別,它只是原本型別的代稱 (完全等價)。- 通常定義在檔案的最上層 (Top Level)。
總結
- 先用
is檢查。 - 享受 Smart Casts 的便利,不用手動轉。
- 如果真的要強轉,盡量用
as?避免崩潰。 - 小心泛型的 Unchecked Casts。