Kotlin学习笔记5-4 其他-类型检查与转换

类型检查转换:is和as

Kotlin官网:Other-Type Checks and Casts

is和!is运算符

要在运行时检查某一对象是否为某个类型使用is运算符判断,相符is,不相符!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)
}

自动转换

某些场景下,Kotlin的编译器可以判断出类型转换是安全的,此时无需显式地转换,可以直接作为对应类型使用,编译器会自动插入适当的转换:
例如is检查后:

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

!is检查后return中断后面代码:

 if (x !is String) return
 print(x.length) // x自动转换为String

短路运算的右侧:

    // 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
    }

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中检查过,自动转换生效;
    当声明为公开的/有自定义getter时自动转换失效;
  • var局部变量:检查完后立刻使用,中间没有被修改,没有在lambda中修改,不是局部委托属性;
  • var属性:无法使用自动转换,任何时候都不行。

不安全的转换

使用as运算符转换,如果类型不符会抛出异常,认为是不安全的:

val x: String = y as String

注意Kotlin区分可空和不可空类型,所以null转换成不可空类型也会抛出异常,如上例y为null时会出错。null可以转换为可空类型:

val x: String? = y as String?

安全(可空)转换

为了防止为空时转换出现异常,可以用as?运算符,为null时返回null:

val x: String? = y as? String

as?运算符的右侧为不可空类型,返回值为可空类型。

类型擦除和泛型类型检查

泛型只在编译时保证类型安全,编译后会被擦除,运行时无法获取泛型信息,具体见3-8泛型。
星号除外,类型投射可以用来做类型检查:

if (something is List<*>) {
    something.forEach { println(it) } // The items are typed as `Any?`
}

带泛型的类自身可以用来检查和转换,泛型的尖括号可以省略:

fun handleStrings(list: List<String>) {
    if (list is ArrayList) {
        // `list` is smart-cast to `ArrayList<String>`
    }
}

转换:list as ArrayList
具体化类型参数的内联函数会在运行时保留泛型信息,arg is T是允许的(T是泛型),泛型实例的类型信息依然会被擦除:

inline fun <reified A, reified B> Pair<*, *>.asPairOf(): Pair<A, B>? {
    if (first !is A || second !is B) return null
    return first as A to second as B
}

val somePair: Pair<Any?, Any?> = "items" to listOf(1, 2, 3)

val stringToSomething = somePair.asPairOf<String, Any>()
val stringToInt = somePair.asPairOf<String, Int>()
val stringToList = somePair.asPairOf<String, List<*>>()
val stringToStringList = somePair.asPairOf<String, List<String>>() // Breaks type safety!

fun main(args: Array<String>) {
    println("stringToSomething = " + stringToSomething)
    println("stringToInt = " + stringToInt)
    println("stringToList = " + stringToList)
    println("stringToStringList = " + stringToStringList)
}

未检查的转换

例:

fun readDictionary(file: File): Map<String, *> = file.inputStream().use { 
    TODO("Read a mapping of strings to arbitrary elements.")
}

// We saved a map with `Int`s into that file
val intsFile = File("ints.dictionary")

// Warning: Unchecked cast: `Map<String, *>` to `Map<String, Int>`
val intsDictionary: Map<String, Int> = readDictionary(intsFile) as Map<String, Int>

程序逻辑保证了类型转换安全,但是Kotlin无法识别,此时会有警告。
在上例中,定义带泛型的类,可以避免调用者进行未检查的转换,将转换逻辑移到具体实现中。
要消除警告,可以使用@Suppress("UNCHECKED_CAST")注解:

inline fun <reified T> List<*>.asListOfType(): List<T>? =
    if (all { it is T })
        @Suppress("UNCHECKED_CAST")
        this as List<T> else
        null

在JVM平台,数组类(Array<Foo>)会保留元素的类型信息,类型转换检查部分生效,元素类型包含的泛型信息依然会被擦除。例如:foo as Array<List<String>?>,foo可以被转换成任何List<*>型数组,List的泛型信息被擦除了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值