大家好,我是青空,今天是kotlin入门系列的第3篇。今天给大家介绍的是kotlin的类型检测和转换。
kotlin 中类型检测使用的是is关键字
“kotlin” is String // 是
“kotlin” !is String //不是
kotlin 中类型转换使用的是as关键字
123 as Long
什么是类型安全
经过类型擦除后,依旧可以通过检测,确保当前的变量类型是确定的某个类型
智能转换
在许多情况下,不需要在 Kotlin 中使用显式转换操作符,因为编译器跟踪不可变值的 is-检测以及显式转换,并在需要时自动插入(安全的)转换
范例
fun demo(x: Any) {
if (x is String) {
print(x.length) // x 自动转换为字符串
}
}
f (x !is String) return
print(x.length) // x 自动转换为字符串
看个复杂点的
// || 右侧的 x 自动转换为字符串
if (x !is String || x.length == 0) return
// && 右侧的 x 自动转换为字符串
if (x is String && x.length > 0) {
print(x.length) // x 自动转换为字符串
}
再看一个
when (x) {
is Int -> print(x + 1)
is String -> print(x.length + 1)
is IntArray -> print(x.sum())
}
换言之,Kotlin只要能通过is判断之后,就可以正常推导出正确的类型,自动转换
注意
- 智能转换需要 变量在检测和使用时,是不可改变的
- val局部变量
- 除了委托属性,都可以
- val属性
- open或自定义getter的不行
- private、internal,检测跟声明属性在同一模块 可以
- var 局部变量
- 没有委托
- 检测和使用之间没有修改,没有可能被修改(传入后被修改也不行)
- var 属性
- 不行
“不安全的”转换操作符
用as进行,会抛出异常
范例
val x: String = y as String
null不能转为String,若y为null,就会抛异常
“安全的”(可空)转换操作符
使用as?来进行,失败返回null
类型擦除与泛型检测
编译器会禁止由于类型擦除而无法执行的 is 检测
范例
fun handleStrings(list: List<*>) {
if (list is List) {
// list 会智能转换为 ArrayList<String>
}
}
这个函数对list进行了类型检测,因为类型擦除,这里就无法编译通过 。报错提示 “can check for instance of erased type: List”
在运行时,泛型类型的实例并未带有关于它们实际类型参数的信息。例如, List 会被擦除为 List<*>
因此你只能 考虑不带类型参数的类型转换,比如 list as List
有具体类型参数的函数,在调用处会内联
范例
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
}
这里first 跟 A在函数创建时,并不知道具体的类型。但在调用时,却能进行类型检测。这就是内联。
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>() // 破坏类型安全!
为什么stringToStringList破坏类型安全呢?因为List的类型会被擦除成List<*>。所以,这里这么写就不合适了。
fun main() {
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.")
}
// 我们已将存有一些 Int 的映射保存到该文件
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>
最后一行会报个warning,“Unchecked cast: Map<String, *> to Map<String, Int>”,类型转换不能在运行是完全检测,也不能保证映射中的值是Int,所以就报了warning。
怎么规避呢?
可以考虑重新设计代码结构。比如,将未受检的类型转换转移到实现细节中。本范例,就将readDictionary的返回值类型改一下
fun readDictionary(file: File): Map<String, Int> = file.inputStream().use {
TODO("Read a mapping of strings to arbitrary elements.")
}
// 我们已将存有一些 Int 的映射保存到该文件
val intsFile = File("ints.dictionary")
// Warning: No cast needed
val intsDictionary: Map<String, Int> = readDictionary(intsFile) as Map<String, Int>
这里就是报No cast needed,将最后一行改成这样
val intsDictionary: Map<String, Int> = readDictionary(intsFile)
这个范例,就直接讲readDIctionary的输出修改了。修改的是实现细节,规避了类型检测不完全的问题。