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

使用和避免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 true
possible.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.

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏余林丰

Effective Java通俗理解(上)

  这篇博客是Java经典书籍《Effective Java(第二版)》的读书笔记,此书共有78条关于编写高质量Java代码的建议,我会试着逐一对其进行更为通俗...

2757
来自专栏张善友的专栏

检查Python对象

编程环境中的对象很象现实世界中的对象。实际的对象有一定的形状、大小、重量和其它特征。实际的对象还能够对其环境进行响应、与其它对象交互或执行任务。计算机中的对象试...

19610
来自专栏全栈架构

抱歉!不要用Java的语法思维来写Kotlin

如果你是像我一样是一名 优秀的Java开发者 ^_^,而且已经想用kotlin来实现你的程序,那么,抱歉!不要用Java的语法思维来写Kotlin,不要让kot...

1314
来自专栏云霄雨霁

查找----基于散列表(拉链法)

3020
来自专栏云霄雨霁

排序----快速排序

1200
来自专栏Jimoer

Java设计模式学习记录-原型模式

1465
来自专栏猿人谷

浅谈C/C++中的指针和数组(一)

                                                       浅谈C/C++中的指针和数组(一)       指...

2175
来自专栏Java架构师进阶

Java程序员实现完美代码的十大要素

对于方法的注释应该包含详细的入参和结果说明,有异常抛出的情况也要详细叙述;类的注释应该包含类的功能说明、作者和修改者。

952
来自专栏Android开发指南

Effecvtive Java Note

3085
来自专栏java达人

最有价值的50道java面试题(二)

来自骆昊的技术专栏 26、抽象的(abstract)方法是否可同时是静态的(static),是否可同时是本地方法(native),是否可同时被synchron...

29210

扫码关注云+社区

领取腾讯云代金券