一、空安全
Java语言中没有C/C++的指针,取而代之的是引用。在Java支持的四种数据类型(类、接口、数组、基本类型)中,类、接口和数组统称为引用类型,定义一个引用类型变量需要两个步骤:
定义一个引用类型的变量。
String s;List<String> l; int[] n;
进行初始化(创建对象并使之前定义的引用指向这个对象)。有两种方法:
用new关键字调用构造方法,创建一个新对象;
使用方法出入一个对象。
s = "Hello World"; // String 类型的特殊写法,等同于调用构造方法l = new ArrayList<>(); // 使用子类型创建对象n = new int[10];
在Java中,对于没有进行初始化的引用类型变量,调用它们的实例方法或实例属性都会产生NullPointerException,也就是所谓的NPE。而这一切都发生在运行时,编译时不会报错。
Kotlin的类型系统诣在从代码中消除NullPointerException。NPE的唯一可能的原因是:
显示调用throw NullPointerException();
使用了下文描述的!!操作符;
外部Java代码导致的;
对于初始化,有一些数据不一致。
Kotlin解决空安全,而不是消灭NPE,而是允许NPE存在,但只会在它该出现的时候出现。Kotlin区分了可空类型与不可空类型。
二、可空类型与非空类型
在Kotlin中如下代码:
val s: String = null
会得到一个编译错误。编译器说不能讲null赋给非空的String类型。
在Kotlin中,所有数据类型默认都是非空的,如果想把null赋给它们的对象,需要在类型后加上?,声明为可空类型。
所以上面的代码需要在String后面加上?:
val s: String? = null
所以,在Kotlin中我们只要将变量声明为非空类型,就可以放心地调用它的实例方法和实例变量,不用担心出现NPE。
三、安全调用符(?.)
对于可空类型的变量,应该怎样安全地调用它的属性和方法呢?Kotlin的解决方式是使用?.安全调用符。
如下面Kotlin的函数:
// 安全调用属性fun printLengthOfString(s: String?) = println(s?.length)
在Java中等同于如下:
public static final void printLengthOfString(@Nullable String s) { Integer var1 = (s != null) ? s.length() : null; System.out.println(var1); }
安全调用操作符(?.)实际上是个语法糖,会被编译为一个三目运算表达式。
如果?.前的对象不为null,则调用?.后的方法或属性,否则就返回null。
如果一个可空变量已经被判断为非空,再次调用它的方法和变量时,会自动将变量转换为非空类型,实现安全调用。
四、Elvis操作符(?:)
这个操作符是三目条件运算符的简略写法。
如果?:左侧表达式非空,Elvis操作符就返回其左侧表达式,否则就返回右侧表达式。请注意,当且仅当左侧为空时,才会对右侧表达式求值。
Kotlin中用Elvis操作符配合安全调用符实现简单清晰的空检查和空操作。
在Java中:
(files != null) ? files.length : "Empty"
在Kotlin中:
files?.size ?: "Empty"
可以如下理解Elvis操作符:
A ?: B 等价于 if(A==null) B
A ?. B ?: C 等价于 if(A!=null) A.B else C
五、let函数
let函数可以看做一个加强版的安全操作符,或者另一种写法if not null then
语句。
let函数单纯地接收一个函数,然后执行它,返回函数的执行结果。一般情况下,let函数与安全调用符配合使用:
val listWithNulls: List<String?> = listOf("A", null)for (item in listWithNulls) { item?.let { println(it) } // 输出 A 并忽略 null}
也就是时候安全调用符+let函数就等同于if not null then语句。
六、非空操作符(!!)
要在Kotlin中产生NPE,有两种办法:
显式地throw NullPointerException();
使用非空操作符!!。
val l = b!!.length
b!!会返回一个非空的b值,如果b为空,就会抛出一个NPE异常。
但是,Kotlin并不建议在代码中广泛使用!!操作符,因为这样会浪费Kotlin良好的非空类型的设计,让Kotlin代码中充斥着NPE。