Kotlin-32.空指针安全/null安全(Null Safety)

官方文档: http://kotlinlang.org/docs/reference/null-safety.html

1.可空与非空类型(Nullable types and Non-Null Types)

很多编程语言(包括Java)最常见的陷阱就是访问null引用,
在java中会导致空指针异常NullPointerException,简称NPE;
因此东尼·霍尔(Tony Hoare),图灵奖得主把Null引用称为十亿美元的错误!

Kotlin类型系统旨在消除null引用的危险,
所以在Kotlin中只有以下情况,才会导致空指针异常NullPointerException:  
    1.显式调用 throw NullPointerException();
    2.使用 !! 操作符;
    3.外部Java代码导致的;
    4.对于初始化,有些数据不一致(如构造函数中未初始化的this用于某个地方);

Kotlin类型系统能区分一个引用可null(可空引用)还是不可null(非空引用)
例如,String类型的常规变量不能为空null:
    var a: String = "abc"
    a = null //编译错误,a不能为null

    //如果要允许变量为空null,需要在类型后添加问号?标记可null:
    var b: String? = "abc"
    b = null //编译OK

    //a不能为null,所以a方法或属性,不会导致NPE(NullPointerException)
    val l = a.length

    //b可以为null,所以访问b属性是不安全的,kotlin编译器会报错
    val l = b.length //kotlin编译器会报: 变量“b”可能为null
    //要想可以访问b,在下文中有几种方法: if检查null, ?.操作符, !!操作符

2.在条件中检查null(Checking for null in conditions)

第一个选择,是显式检查b是否为null,编译器会跟踪所执行检查null:
    val l = if (b != null) b.length else -1 //编译OK
    val l = b.length //编译报错

    if (b != null && b.length > 0) {
        print("String of length ${b.length}")
    } else {
        print("Empty string")
    }

提示: 在条件中检查null,只适用于b是不可变量的情况
    (即局部变量在检查null和使用之间没有改变,
    或者成员变量val有幕后字段backing field且不可覆盖overridable),
    否则可能发生在检查之后b又变为null的情况!

3.安全调用(Safe Calls)-操作符?.

第二个选择,是使用安全调用操作符?.
如果b非空,则返回b.length,否则返回null(表达式返回类型是Int?)
    val l = b?.length //编译OK
    val l = b.length //编译报错
    
安全调用?.在链式调用中很有用,
例如,一个员工Bob可能分配给一个部门,该部门可能有一个负责人(名字):
    //以下任意变量为null空,该链式调用结果就返回null
    bob?.department?.head?.name

如果只对非空值执行操作(即忽略null),安全调用操作符?.可与let一起使用:
    val listWithNulls: List<String?> = listOf("A", null)
    for (item in listWithNulls)
        item?.let { println(it) } //输出A,(忽略null)

4.?:操作符(Elvis Operator)

除了用完整if表达式检查null,还可用Elvis操作符?:简化if表达式,
如果?:左侧表达式非空,就返回其左侧表达式,否则返回其右侧表达式
    val l = b?.length ?: -1 //当且仅当?:左侧为null时,才对右侧表达式求值
    //等价于
    val l: Int = if (b != null) b.length else -1

因为在Kotlin中throw,return都是表达式,所以也可在?:右侧,例如检查函数参数:
    fun foo(node: Node): String? {
        val parent = node.getParent() ?: return null
        val name = node.getName() ?: throw IllegalArgumentException("name expected")
        //...
    }

5.!!操作符(!! Operator)

第三个选择,是使用操作符!!,为NPE空指针异常的爱好者准备的, 让编译器不检查null
如果b变量为null,就会抛出空指针异常NullPointerException(NPE)!
例如:
    val l = b!!.length //如果b为null,会抛出空指针异常NullPointerException

6.过滤集合的可空元素-filterNotNull()

如果集合有可空类型元素的,要过滤非空元素,可用filterNotNull实现:
    val nullableList: List<Int?> = listOf(1, 2, null, 4)
    val intList: List<Int> = nullableList.filterNotNull()

简书:http://www.jianshu.com/p/f2da6e3eb53e CSDN博客: http://blog.csdn.net/qq_32115439/article/details/74509791
GitHub博客:http://lioil.win/2017/07/05/Kotlin-null.html
Coding博客:http://c.lioil.win/2017/07/05/Kotlin-null.html