Kotlin 类型检查与转换

在 Koltin 中,开发者可以在运行时检查一个对象的类型,并且 也可以将一个对象从一个类型转为另外一个类型。

is 和 !is 操作符

使用 is 和 !is 操作符可以检查一个对象是否是开发者给定的类型:

kotlin 复制代码
if (obj is String) {
    print(obj.length)
}

if (obj !is String) { // Same as !(obj is String)
    print("Not a String")
} else {
    print(obj.length)
}

智能转换

大多数情况下,由于编译器能够进行自动类型转换,开发者不需要使用强制类型转换。编译器会追踪不可变类型的类检查和类型转换,并且在必要的时候进行隐式的安全转换:

kotlin 复制代码
fun demo(x: Any) {
    if (x is String) {
        print(x.length) // x is automatically cast to String
    }
}

编译器甚至非常智能,如果一个否定检查导致了返回,那么进行安全的类型转换:

kotlin 复制代码
if (x !is String) return

print(x.length) // x is automatically cast to String

控制流

智能类型转换不仅作用于 if 控制,还作用于 when 表达式和 while 循环:

kotlin 复制代码
when (x) {
    is Int -> print(x + 1)
    is String -> print(x.length + 1)
    is IntArray -> print(x.sum())
}

如果开发者在 if,when,或者 while 条件之前,就声明了一个布尔类型的变量,那么编译器会收集找个变量的任何信息,这样的话在相应的代码块中都可用智能类型转换。

这样的话,开发者提取布尔条件到变量中的时候,就很有用。开发者可以给变量起一个有意义的名字,这样会提高代码的阅读性,并且在之后的代码中可以重复使用:

kotlin 复制代码
class Cat {
    fun purr() {
        println("Purr purr")
    }
}

fun petAnimal(animal: Any) {
    val isCat = animal is Cat
    if (isCat) {
        // The compiler can access information about
        // isCat, so it knows that animal was smart-cast
        // to the type Cat.
        // Therefore, the purr() function can be called.
        animal.purr()
    }
}

fun main(){
    val kitty = Cat()
    petAnimal(kitty)
    // Purr purr
}

逻辑运算

如果在 if 语句的左侧有类型检查,那么编译器在 && 或者 || 运算符右侧可以进行智能类型转换:

kotlin 复制代码
// x is automatically cast to String on the right-hand side of `||`
if (x !is String || x.length == 0) return

// x is automatically cast to String on the right-hand side of `&&`
if (x is String && x.length > 0) {
    print(x.length) // x is automatically cast to String
}

如果开发者将对象的类型检查和 || 运算符结合使用,那么会将它们智能转换为它们最接近的共同超类:

kotlin 复制代码
interface Status {
    fun signal() {}
}

interface Ok : Status
interface Postponed : Status
interface Declined : Status

fun signalCheck(signalStatus: Any) {
    if (signalStatus is Postponed || signalStatus is Declined) {
        // signalStatus is smart-cast to a common supertype Status
        signalStatus.signal()
    }
}

内联方法

编译器可以对传递给内联函数的 lambda 函数中捕获的变量进行智能转换。

内联函数会被视为具有隐式的 callsInPlace 约定。也就是说,传递给内联函数的所有 lambda 函数都会就地调用(即 lambda 函数的执行代码会直接嵌入到内联函数被调用的位置,而非单独创建函数对象后再调用)。由于 lambda 函数是就地调用的,编译器能够确定:该 lambda 函数不会泄露其函数体内包含的任何变量的引用(即变量不会被传递到 lambda 外部、超出当前作用域的地方使用)。

编译器会利用这一特性,结合其他分析手段,判断对捕获的变量进行智能转换是否安全。例如:

kotlin 复制代码
interface Processor {
    fun process()
}

inline fun inlineAction(f: () -> Unit) = f()

fun nextProcessor(): Processor? = null

fun runProcessor(): Processor? {
    var processor: Processor? = null
    inlineAction {
        // The compiler knows that processor is a local variable and inlineAction()
        // is an inline function, so references to processor can't be leaked.
        // Therefore, it's safe to smart-cast processor.

        // If processor isn't null, processor is smart-cast
        if (processor != null) {
            // The compiler knows that processor isn't null, so no safe call
            // is needed
            processor.process()
        }

        processor = nextProcessor()
    }

    return processor
}

异常处理

智能转换信息会传递到 catch 、 finally 块中。由于编译器会跟踪对象是否为可空类型,这一特性能让你的代码更安全。例如:

koltin 复制代码
fun testString() {
    var stringInput: String? = null
    // stringInput is smart-cast to String type
    stringInput = ""
    try {
        // The compiler knows that stringInput isn't null
        println(stringInput.length)
        // 0

        // The compiler rejects previous smart cast information for 
        // stringInput. Now stringInput has the String? type.
        stringInput = null

        // Trigger an exception
        if (2 > 1) throw Exception()
        stringInput = ""
    } catch (exception: Exception) {
        // The compiler knows stringInput can be null
        // so stringInput stays nullable.
        println(stringInput?.length)
        // null
    }
}

智能类型转换前提

val 局部变量 在所有情况下都适用,局部委托属性除外
val 属性 如果属性是 private(私有)、internal(内部)的,或者类型检查是在声明该属性的同一个模块中执行的,那么智能转换是可以的。但是智能类型转换不能用于open属性和用于自定义getter的属性
var 局部变量 在类型检查(如 is 判断)与变量使用之间,未被修改;未被捕获到会修改该变量的 lambda 函数中;不是局部委托属性。
var 属性 不适用

不安全的转换操作符

使用不安全的转换操作符 as ,可以将一个对象转为一个非空的类型:

koltin 复制代码
val x: String = y as String

该转换会爆出异常,所以是不安全的。

如果 y 是 null 的,那么上面的代码就会出异常。这是因为 null 不可以转为 String。如果想要转一个可能为 null 的值,可以使用可空的类型:

kotlin 复制代码
val x: String? = y as String?

安全的转换操作符

如果不想要出异常,可以使用 as? 操作符,这样的话转换失败的时候,会返回 null:

kotlin 复制代码
val x: String? = y as? String

原文链接

相关推荐
Wgllss37 分钟前
完整烟花效果,Compose + 协程 + Flow + Channel 轻松实现
android·架构·android jetpack
扛麻袋的少年41 分钟前
6.Kotlin的Duration类
android·开发语言·kotlin
独自破碎E41 分钟前
得物25年春招-安卓部分笔试题1
android
雨白2 小时前
Android 自定义 View:精通文字的测量与高级排版
android
Jasonakeke2 小时前
【重学MySQL】八十八、8.0版本核心新特性全解析
android·数据库·mysql
闲暇部落5 小时前
android studio配置 build
android·android studio·build
_祝你今天愉快5 小时前
Android FrameWork - Zygote 启动流程分析
android
龙之叶7 小时前
Android系统模块编译调试与Ninja使用指南
android
源码哥_博纳软云8 小时前
JAVA国际版多商户运营版商城系统源码多商户社交电商系统源码支持Android+IOS+H5
android·java·ios·微信·微信小程序·小程序·uni-app