is and !is operators

is 用于执行运行时确定对象是否符合给定类型。

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

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

smart casts

核心原理

编译器自动跟踪类型检查结果,无需手动强制转换,直接使用目标类型的属性和方法。

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

支持的控制流

  • if  条件:通过  is  或  !is  检查后,块内自动转换。
  • when  表达式:根据不同类型分支自动转换。
when (x) {
    is Int -> print(x + 1)
    is String -> print(x.length + 1)
    is IntArray -> print(x.sum())
}

while  循环:只要类型检查条件成立,循环内保持转换状态。

特殊场景

逻辑操作符(&&/||

  • 左侧为类型检查时,右侧自动转换。
if (x is String && x.length > 0) {
    print(x.length)  // x 自动转换为 String
}
if (x !is String || x.length == 0) return  // x 在右侧自动转换为 String

变量提取

将类型检查结果存入布尔变量,仍可触发智能转换。

fun petAnimal(animal: Any) {
    val isCat = animal is Cat
    if (isCat) {
        animal.purr()  // 编译器通过 isCat 推断 animal 是 Cat 类型
    }
}

类型转换操作符

不安全转换(as

  • 语法obj as Type,若转换失败抛出  ClassCastException
  • 注意:若目标类型不可为空(如  String),源对象为  null  时也会抛异常。
val x: String = y as String  // 若 y 为 null 或非 String 类型,抛出异常

  • 安全用法:搭配可空类型使用。
val x: String? = y as String?  // 若 y 为 null,x 为 null;若 y 非 String,抛异常

安全转换(as?

  • 语法obj as? Type,转换失败返回  null,不抛异常。
  • 应用场景:处理可能转换失败的情况,避免程序崩溃。
val x: String? = y as? String  // 若 y 非 String 或为 null,x 为 null

其他重要场景

  • 内联函数(Inline Functions)中的智能转换

内联函数会将 lambda 代码直接嵌入调用处,编译器可保证变量引用不会 “泄漏”,因此可对 lambda 中捕获的变量进行智能转换。

inline fun <T> processIfNotNull(obj: T?, action: (T) -> Unit) {
    if (obj != null) {
        action(obj)  // 智能转换 obj 为非空类型
    }
}

fun runProcessor() {
    val processor: Processor? = getProcessor()
    processIfNotNull(processor) { proc ->
        proc.process()  // 直接调用,无需安全操作符
    }
}
  • 异处理中的智能转换

try  块中若变量被赋值为非空,编译器会记住其类型;若变量在  try  块中被设为  null,则取消智能转换。

catch  块中变量恢复为可空类型,需使用安全操作符(?.)。

fun testString() {
    var stringInput: String? = ""  // 智能转换为 String
    try {
        println(stringInput.length)  // 非空,直接访问
        stringInput = null  // 取消智能转换,变为 String?
        // ...
    } catch (e: Exception) {
        println(stringInput?.length)  // 需用安全操作符
    }
}