Kotlin相对于Java来说,有一个显著的特点,就是它致力于消除空引用所带来的危险,在Java中,为了避免NullPointerException
的出现,我们需要不厌其烦的使用if (value != null) {}
来处理这种问题(虽然在JDK8之后有了更好的方式)
在Kotlin中很好解决了这个问题,下面来看看它是如何做到的。
数据类型
数据类型分为非空类型
和可空类型
- 非空类型:Char, Boolean, Byte, Short, Int,Long, Float, Double,String
- 可空类型:Char?, Boolean?, Byte?, Short?, Int?,Long?, Float?, Double?, String?

上图这段代码中,User对象中的name,email字段都定义成了非空类型,在下面初始化值时,就不能赋值为null
简直太粗暴了,但是至少现在看来,我们不需要在使用user.email时去判断是否为空了。
空安全运算符
安全调用(?.)
继续改进上段代码,将email
的类型定义为String?
,这样就能赋值为null了,但是使用呢?又回到了if判空的尴尬局面吗?显然不是
1
2
3
4
5
6
7
8
9
10
11
12
|
open class User(val name: String, val email: String?)
fun testNullUserEmail() {
var user = User("张三", null)
var emailLength = user.email?.length
println("length = $emailLength") // length = null
}
fun main(args: Array<String>) {
testNullUserEmail()
}
|
改进后的代码使用了?.
安全调用,因为email为空,则不再调用length
属性,而是直接返回了null,现在可以肯定的讲,这段代码一辈子都不会抛出NullPointerException
了。
Elvis运算符(?:)
还是那段代码,如果user.email为null,我想干一些其他的事情咋办?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
import java.lang.IllegalArgumentException
/** * @author wuwenze * @date 2019-05-10 */
open class User(val name: String, val email: String?)
fun testNullUserEmail() {
var user = User("张三", null)
// var emailLength = user.email?.length
var emailLength = user.email?.length ?: 0 // 如果为空, 返回0
println("length = $emailLength")
// 或者抛出一个预定义的异常
var len = user.email?.length ?: throw IllegalArgumentException("user.email is null")
// 执行其他的逻辑操作?
var len2 = user.email?.length ?: println("user.email is null")
println("len2 = $len2") // len2 = kotlin.Unit
// 中断函数的执行?
var len3 = user.email?.length ?: return
}
fun main(args: Array<String>) {
testNullUserEmail()
}
|
非空断言运算符(!!)
再来看一段代码:定义类型为可空类型(String?)的name字符串

下面在调用length时,居然编译不通过了,说可能有空指针的风险(真是安逸啊)
现在开发者明确的知道,name不可能为空,有两种办法,一是修改数据类型为非空类型,二是使用!!
运算符把任何值转换成非空类型
1
2
3
4
5
6
|
fun main(args: Array<String>) {
var name :String? = "hello"
var len = name!!.length
println("$name length is $len") // hello length is 5
}
|
安全类型转换(as?)
我们知道类型转换可能产生ClassCastException
异常,例如:
1
2
|
var a: Long = 1
val aInt: Int? = a as Int // java.lang.ClassCastException
|
使用as?
优化一下代码即可解决
1
2
|
var a: Long = 1
val aInt: Int? = a as? Int // 如果类型转换不成功,返回null
|
let函数(.let)
再来看一段代码, 这段代码中,定义了一个getNullUser函数,这段代码可能返回空,那么在之前java的做法可能类似于这样,尽管我现在用Kotlin如此简洁的语言来写,还是感觉诺里啰嗦,更别说Java了
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
open class User(val name: String?, val email: String?)
fun getNullUser(): User? = null
fun main(args: Array<String>) {
val user = getNullUser()
if (user != null && user.name != null) {
println("user.name = ${user.name}, user.name.len = ${user.name.length}")
}
if (user != null && user.email != null) {
println("user.email = ${user.email}, user.email.len = ${user.email.length}")
}
}
|
使用let函数改造一下吧:
1
2
3
4
5
6
7
8
9
10
|
open class User(val name: String?, val email: String?)
fun getNullUser(): User? = null
fun main(args: Array<String>) {
getNullUser().let {
println("user.name = ${it?.name}, user.name.len = ${it?.name?.length}")
println("user.email = ${it?.email}, user.email.len = ${it?.email?.length}")
}
}
|
这段代码不会有任何的输出,因为getNullUser()函数始终返回的是一个NULL,let代码块里面的代码不会被执行。
评论