Overview
本节主要介绍各种语言中的空值类型和如何处理空值
Java 篇
null
Java 使用关键字 null
表示空值(没有任何引用),使用 ==
来判断一个值是否是空值。
Integer x = null;
System.out.println(x == null);
NullPointerException
Java 在 1.8 之前对于空指针异常没有什么比较好的处理方式,你必须在每个可能为空值的地方手动进行 if-else
的判断或者使用诸如 Google 的 guava
的 Option
类那样的第三方库。而 Java 在 1.8 版本后在语言层面引入了 Optional
类从而可以不需要第三包就能直接实现同样的功能。
Java 1.8 以前
int result;
// 非空处理
if (x != null) {
result = 3 + x;
}
// 空值处理
else {
result = 0;
}
Java 1.8
Optional nullableX = Optional.ofNullable(x);
// 非空处理
result = 3 + nullableX.orElseGet(new Supplier() {
// 空值处理
public Integer get() {
return 0;
}
});
也可以像以下方式一样将 Optional 用于集合中
Java 1.8 以前
List persons = new ArrayList() {{
add(null);
add(new Person("Peter"));
}};
for (Person p : persons) {
if (p != null) {
p.getName();
}
}
Java 1.8
List> opPersons = new ArrayList>() {{
Person p = null;
add(Optional.ofNullable(p));
add(Optional.ofNullable(new Person("Peter")));
}};
for (Optional p : opPersons) {
// 判断是否非空
if (p.isPresent()) {
System.out.println(p.get().getName());
}
}
使用 Optional 可以很好的解决空指针异常的问题,不过其语法本身在有些情况却未必比直接使用 if
判断要优美多少。
Groovy 篇
null
Groovy 也使用关键字 null
表示空值。并且可以使用 ==
或 is()
来判断一个值是否是空值。
Integer x = null
println(x == null)
println(x.is(null))
NullPointerException
除了 Java 的方法,Groovy 还可以使用安全操作符 ?.
来避免调用空引用的方法时产生的空指针异常。
List persons = [null, new Person("Peter")]
for (Person p : persons) {
println(p?.name)
}
如果调用安全操作符 ?.
的对象不存在时,应用会放弃调用指定的方法
Scala 篇
null
Scala 表示无的概念非常复杂。其中最常见的空值也是使用 null
表示,使用 ==
进行判断。
val x = null
println(x == null)
NullPointerException
Scala 内置了 Option
类,使用方法类似上文提到的 Java 1.8 提供的 Optional
类。
例:
val persons: List[Option[Person]] = List(None, Option(new Person("Peter")))
for (p <- persons) {
if (p isDefined) {
println(p.get.name)
}
}
实际上当创建 Option 对象时,场景的是其子类 Some
或者 None
,一个表示有值,一个表示空值。
无
Scala 有以下几种表示无的方式,可以说数量多得让人恼火,只能留在后面的章节来说。
- Null 代表一个 Trait,trait 是什么之后的章节再说,现在先理解为类
- null Null 的实例,由于 Scala 中一切皆对象,所有空值本身也是一种对象
- Nil 空列表,表示列表本身虽然存在但是没有任何内容
- Nothing 一个 Trait,是 Scala 中所有类的子类
- None Option 的子类,用于表示返回值为空
- Unit 表示没有返回值
Kotlin 篇
null
Kotlin 也使用 null
表示空值,对于一般对象使用 ==
进行空值的判断,对于字符串还提供了 isNullOrEmpty()
和 isNullOrBlank()
两个方法。
var x = null
println(x == null)
// 判断是否为 null 或空字符串
println(x.isNullOrEmpty())
// 判断是否为 null 或空字符串或只包含 whitespace 字符
println(x.isNullOrBlank())
NullPointerException
Nullable 和 Non-Null
Kotlin 处理空值的方式与其它几种语言都不一样,它将变量本身分为 Nullable
和 Non-Null
两种类型,前一个类型的值可以为空值,后一个类型则者不能指定为空值。
默认情况下声明时变量时如果没有指定类型的话则为 Non-Null
类型,指定类型的变量则为 Nullable
类型。
例:
// Nullable
var x = null
// Non-Null
var nonNullString: String = "foo"
// Wrong!
// nonNullString = null
使用符号 ?
可以将一个指定类型的变量声明为 Nullable
var nullableString: String? = "foo"
nullableString = null
这种做法的好处是调用方法时通过返回值类型就可以判断返回值是否可以为空,从而避免无谓的空值判断。
安全操作符
同 Groovy 一样,Kotlin 也提供了 ?.
这样的安全操作符
val persons = listOf(null, Person("Peter"))
for (p in persons) {
println(p?.name)
}
结合 Nullable
可以写出如下代码来防止空指针异常
var y: Int? = null
val nullableResult: Int? = y?.plus(3)
println(nullableResult)
!! 操作符
有这么一个场景,当我们为一个 Nullable
类型的变量赋值后其值已经变成非空了,但是如果想将其赋值给其它变量时仍然需要将其它变量声明为 Nullable
,这未免有些多此一举。所以 Kotlin
提供了 !!
操作符,用于将 Nullabel
转换为 Not-null
类型。
var y: Int? = null
y = 10
val nullableSum: Int? = y?.plus(3)
val nonNullSum: Int = y!!.plus(3)
Safe Case
当使用 as
进行类型转换时,如果无法转换到目标类型的话,系统会抛出类型转换异常。这时可以使用 as?
操作符,当遇到无法转换时会返回 null
来代替抛出异常。
val xInt: Int? = x as? Int
Summary
- Java 1.8 和 Scala 都提供了 Option 操作
- Groovy 和 Kotlin 都提供了安全操作符
- Kotlin 提供了
Nullable
和Non-null
类型来解决空指针异常
文章源码见 https://github.com/SidneyXu/JGSK 仓库的 _09_null
小节