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

原文链接

相关推荐
千里马学框架2 天前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台2 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone2 天前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
致远ccc2 天前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
ttyyttemo2 天前
Kotlin 协程中的 Job 结构化并发与取消
android
sun0077003 天前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
ai2work3 天前
ch23 综合复刻:从零做一个最小可用版本(capstone)
kotlin
其实防守也摸鱼3 天前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
ai2work3 天前
ch21 签名、校验与发版
kotlin
AFinalStone3 天前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui