2.7空安全类型
许多编程语言(包括 Java)中最常见的陷阱之一是访问空引用的成员,导致空引用异常。在 Java 中, 这等同于 NullPointerException 或简称 NPE 。
Kotlin 的类型系统旨在从我们的代码中消除 NullPointerException 。NPE 的唯一可能的原因可能是
- 显式调用 throw NullPointerException()
- 使用了下文描述的 !! 操作符
- 外部 Java 代码导致的
- 对于初始化,有一些数据不一致(如一个未初始化的 this 用于构造函数的某个地方)
可空类型与菲空类型
之前提到过,在Kotlin中定义一个允许为空的变量定义和不允许为空的定义方式是不一样的。
var a: String ="abc"//不能为空
//a = null // 编译错误
var b: String? = "abc"//可以为空
//b.length//可能会报错
b = null // ok
第二个就比第一个多了一个?
。
空值检查
现在,如果你调用 a 的方法或者访问它的属性,它保证不会导致 NPE ,这样你就可以放心地使用:
val l = a.length
而b是允许为空的,使用时需要判空
val l = if (b != null) b.length else -1 //定义b可以为空,使用时需要判空
安全调用
var l = b?.length
println("b.length = " + l) //b.length = 3
如果 b 非空,就返回 b.length ,否则返回 null,这个表达式的类型是 Int? 。
b=null
var l2 = b?.length
println("b.length = " + l2) //b.length = null
安全调用在链式调用中很有用。例如,如果一个员工 Bob 可能会(或者不会)分配给艺个部门, 并且可能有另外一个员工是该部门的负责人,那么获取 Bob 所在部门负责人(如果有的话)的名字,我们写作:
bob?.department?.head?.name
如果任意一个属性(环节)为空,这个链式调用就会返回 null
。
如果要只对非空值执行某个操作,安全调用操作符可以与 let
一起使用:
val listWithNulls: List<String?> = listOf("A", null)
for (item in listWithNulls) {
item?.let { println(it) } // 输出 A 并忽略 null
}
Elvis 操作符 :?:
如果 ?: 左边表达式非空,elvis操作符就会返回左边的结果,否则返回右边的结果。 请注意,仅在左侧为空的时候,右侧的表达式才会计算。
上面空值检查的语句就可以写成:
val l3 = b?.length ?: -1
println("b.length = " + l3) //b.length = -1
请注意,因为 throw 和 return 在 Kotlin 中都是表达式,所以它们也可以⽤在 elvis 操作符右侧。这可能会非常方便,例如,检查函数参数:
fun foo(node: Node): String? {
val parent = node.getParent() ?: return null
val name = node.getName() ?: throw IllegalArgumentException("name expected")
// ……
}
!!操作符
第三种选择是为 NPE 爱好者准备的。我们可以写 b!!
,这会返回一个非空的 b
值 (例如:在我们例子中的 String )或者如果 b
为空,就会抛出一个
NPE 异常:
b = null
val l = b!!.length //Exception in thread "main" kotlin.KotlinNullPointerException
因此,如果你想要一个 NPE,你可以得到它,但是你必须显式要求它,否则它不会不期而至。
安全的类型转换
如果对象不是目标类型,那么常规类型转换可能会导致 ClassCastException
。 另一个选择是使用安全的类型转换,如果尝试转换不成功则返回null
:
val aInt: Int? = a as? Int
可空类型的集合
如果你有一个可空类型元素的集合,并且想要过滤非空元素,你可以使用filterNotNull
来实现。
val nullableList: List<Int?> = listOf(1, 2, null, 4)
val intList: List<Int> = nullableList.filterNotNull()
最后附上示例项目Github
[上一篇:2.6字符模板] [返回目录] [下一篇:2.8智能转换]