Guava 指南 之「使用和避免 null」

来源:互联网 发布:数据库工程师在哪报名 编辑:程序博客网 时间:2024/05/13 02:50

使用和避免null

null,糟糕透啦!” —— Doug Lea.
“我称null为百亿美金的错误!” —— C. A. R. Hoare.

轻率地使用null可能导致很多令人惊愕的问题。通过研究谷歌的代码,我们发现:95% 的集合不接受null作为元素,因此相比于默默地接受null,使用快速失败的操作拒绝null值对开发者更有帮助。

此外,null的模糊性会让人很不爽。我们很难知道返回值是null代表着什么意思,例如当Map.get(key)返回null时,既可能是 Map 中对应key的值是null,也可能是 Map 中根本就没有对应key的值。null可以表示成功,也可以表示失败,几乎意味着任何事情。使用除null之外的某些其他值,可以让你表达的含义更清晰。

在某些场景下,使用null也确实是正确的。例如,在内存和速度方面,null就是廉价的,而且在对象数组中,出现null也是不可避免的。但是相对于库来说,在应用代码中,null往往是导致混乱、疑难问题和含义模糊的根源。就像我们上面谈到的,当Map.get(key)返回null时,既可能是 Map 中对应key的值是null,也可能是 Map 中根本就没有对应key的值。最关键的是,null根本就没有给出空值到底意味着什么。

正是由于这些原因,很多的 Guava 工具类都被设计为针对null是快速失败的,除非工具类本身对null是友好的。此外,Guava 提供了很多工具类,可以让我们在必须使用null时用起来更简单,也可以让我们避免使用null.

具体案例

不要在Set中使用null,也不要把null作为 Map 的键;在查询操作中,使用一个特殊值表示null,这会让我们的语言更加清晰。

如果你想使用null作为 Map 中某个键的值,最好不要这么做;单独的维护一个键为空或者非空的Set更好一些。毕竟 Map 中对应于某个键的值为空,或者根本就没有值,这是很容易混淆的情况。因此,最好的方法就是将这些键分开,并且仔细想想,在你的应用中,值为null的键到底有什么含义。

如果你在List中使用null,并且列表是稀疏的,那么使用Map<Integer, E>可能会更高效,并且可能更符合你潜在的需求。

此外,我们可以考虑一下使用自然的null对象的情况。虽然这样的情况并不多,但还是有的,例如有一个枚举类型,添加了一个常量来表示null。还例如,在java.math.RoundingMode里面有一个常量UNNECESSARY,它表示一种不做任何舍入操作的模式,如果用这种模式做舍入操作,则会抛出异常。

如果你确实需要使用null值,并且使用 Guava 的集合会有一些问题,那么你可以选择其他的实现。例如,使用 JDK 中的Collections.unmodifiableList代替 Guava 中的ImmutableList.

Optional

一般情况下,我们使用null表示某种缺失的情况:或许在某个值应该存在的地方,没有值,或者根本就找不到对应的值。例如,通过 Map 的键来获取值的时候,如果对应于某个键的值不存在,Map.get就会返回null.

Optional<T>是一个用非空的值代替引用T有可能为空的方法。一个Optional可能包括非空的T引用(在这种情况下,我们称之为“引用存在”),也可能什么都不包含(在这种情况下,我们称之为“引用缺失”)。但无论如何,Optional绝不会说它包含null.

Optional<Integer> possible = Optional.of(5);possible.isPresent(); // returns truepossible.get(); // returns 5

Optional不打算直接模拟其他编程环境中的option or maybe语义,尽管它们确实有些相似。

在这里,我们列出了一些最常见的Optional操作。

创建Optional实例

这里给出的都是Optional的静态方法。

方法 描述 Optional.of(T) 创建值非空的Optional实例,如果值为空则快速失败 Optional.absent() 返回某些类型引用缺失的Optional实例 Optional.fromNullable(T) 将可能为空的引用传入Option实例,如果引用非空则表示存在;引用为null,则表示缺失

查询方法

下面都是非静态的方法,因此需要特定的Optional<T>实例来调用。

方法 描述 boolean isPresent() 如果Optional包含非null的引用,则返回true T get() 返回Optional所包含的实例,若引用缺失,则抛出java.lang.IllegalStateException T or(T) 返回Optional所包含的引用,若引用缺失,返回指定的值 T orNull() 返回Optional所包含的引用,若引用缺失,返回null. 此为fromNullable的逆操作。 Set<T> asSet() 返回Optional所包含引用的单例不变集合,如果引用存在,返回一个只有单一元素的集合;如果引用缺失,返回一个空集合。

除了上面给出的方法之外,Optional还提供了很多有用的工具方法,具体可以通过Javadoc来查看详细的资料。

使用Optional有什么意义?

除了增加null的可读性之外,Optional最大的优点就在于它是一种傻瓜式的防御机制。如果你想让你的程序编译通过,那么它就会强迫你去积极思考引用缺失的情况。使用null很容易让我们忽略某些情况,尽管FindBugs可以给我们提供帮助,但我们并不认为这种解决方法很好。

特别地,当你返回一个值的时候,既可以是引用存在也可以是引用缺失。你(或者其他人)更容易忘记other.method(a, b)可以返回一个空值,就像你在实现一个方法other.method的时候,你也可能忘记参数a可以是一个null值一样。而将方法的返回类型指定为Optional,也可以迫使调用者思考返回为引用缺失的情形。

便利的方法

当你想使用某些默认值代替一个null值的时候,可以使用MoreObjects.firstNonNull(T, T)方法。就像这个方法的名字提示的一样,如果输入的两个参数值都为null,则会抛出NullPointerException异常。如果你使用Optional的话,这里有一个更好的替换方案,例如first.or(second)

Strings类中,也提供了很多可以处理String值可能为空的方法。特别得,我们提供了恰当的名称:

方法签名 emptyToNull(String) isNullOrEmpty(String) nullToEmpty(String)

我们要强调的是,这些方法主要用来与一些不友好的 API 进行交互,例如null字符串和空字符串等等。每当你写下混淆null和空字符串的时候,Guava 团队的成员都泪流满面。正确的做法是将空字符串和null字符串区别对待,但如果把两者同等对待,这就是要出 bug 的节奏啊!


翻译声明:本文翻译自 GitHub,Google Guava - UsingAndAvoidingNullExplained.


———— ☆☆☆ —— 返回 -> Guava 中文指南 <- 目录 —— ☆☆☆ ————

阅读全文
1 0
原创粉丝点击