Edit Page

类型检查与类型转换

最终更新: 2024/03/21

在 Kotlin 中, 你可以进行类型检查, 在运行时检查一个对象的类型. 类型转换可以将对象转换为另一个类型.

关于 泛型 的类型检查和转换, 例如 List<T>, Map<K,V>, 请参见 泛型的类型检查和转换.

is 与 !is 操作符

可以使用 is 操作符, 或者它相反的 !is 操作符, 在运行时检查一个对象与一个给定的类型是否一致:

if (obj is String) {
    print(obj.length)
}

if (obj !is String) { // 等价于 !(obj is String)
    print("Not a String")
} else {
    print(obj.length)
}

智能类型转换

大多数情况下, 在 Kotlin 中你不必使用显式的类型转换操作, 因为编译器会对不可变值的 is 检查和显式的类型转换 进行追踪, 然后在需要的时候自动插入(安全的)类型转换:

fun demo(x: Any) {
    if (x is String) {
        print(x.length) // x 被自动转换为 String 类型
    }
}

如果一个相反的类型检查导致了 return, 此时编译器足够智能, 可以判断出转换处理是安全的:

if (x !is String) return

print(x.length) // x 被自动转换为 String 类型

对于 &&|| 操作符, 如果在操作符左侧进行了适当的(通常的或相反的)类型检查, 那么操作符的右侧也是如此:

// 在 `||` 的右侧, x 被自动转换为 String 类型
if (x !is String || x.length == 0) return

// 在 `&&` 的右侧, x 被自动转换为 String 类型
if (x is String && x.length > 0) {
    print(x.length) // x 被自动转换为 String 类型
}

智能类型转换(Smart Cast) 对于 when 表达式while 循环 同样有效:

when (x) {
    is Int -> print(x + 1)
    is String -> print(x.length + 1)
    is IntArray -> print(x.sum())
}

注意, 在类型检查语句与变量使用语句之间, 只有在编译器能够确保变量不会改变的情况下, 智能类型转换才是有效的.

在以下条件下可以使用智能类型转换:

val 局部变量 永远有效, 但 局部的委托属性 例外.
val 属性 如果属性是 private 的, 或 internal 的, 或者类型检查处理与属性定义出现在同一个 模块(module) 内, 那么智能类型转换是有效的. 对于 open 属性, 或存在自定义 get 方法的属性, 智能类型转换是无效的.
var 局部变量 如果在类型检查语句与变量使用语句之间, 变量没有被改变, 而且它没有被 Lambda 表达式捕获并在 Lambda 表达式内修改它, 并且它不是一个局部的委托属性, 那么智能类型转换是有效的.
var 属性 永远无效, 因为其他代码随时可能改变变量值.

"不安全的" 类型转换操作符

如果类型转换不成功, 类型转换操作符通常会抛出一个异常. 因此, 称为 不安全的(unsafe) 类型转换. 在 Kotlin 中, 不安全的类型转换使用中缀操作符 as:

val x: String = y as String

注意, null 不能被转换为 String, 因为这个类型不是 可为 null 的(nullable). 如果 y 为 null, 上例中的代码将抛出一个异常. 为了让这段代码能够处理 null 值, 需要在类型转换操作符的右侧使用可为 null 的类型:

val x: String? = y as String?

"安全的" (nullable) 类型转换操作

为了避免抛出异常, 请使用 安全的 类型转换操作符 as?, 当类型转换失败时, 它会返回 null.

val x: String? = y as? String

注意, 尽管 as 操作符的右侧是一个非 null 的 String 类型, 但这个转换操作的结果仍然是可为 null 的.