[Guava官方文档翻译] 2.使用和避免使用null (Using And Avoiding Null Explained)
本文地址:http://www.cnblogs.com/hamhog/p/3536647.html
"null很恶心。" -Doug Lea
"这是一个令我追悔莫及的错误。" - Sir C. A. R. Hoare, 在评价他对null的发明时说。
使用和避免使用null
粗心地使用null能导致各种各样的bug。通过研究Google code base,我们发现大约95%的collection中不该含有null值。对于开发者来说,collection对null值如果能直接报错,会比默默接受更有帮助。
此外,null让人讨厌的一点在于它的含义模糊。函数返回值为null时,很少能让人明白是什么意思——比如,Map.get(key)返回null,可能是因为map中的值为null,也可能是因为map中没有对应的值。Null可以表示失败,可以表示成功,可以表示任何事情。想要表意清晰,就不要用null。
虽然这么说,也有时候用null是正确的选择。null在时间和空间上的代价都很低,并且在object array中不可避免。但是相对于官方库,在实际应用的代码中,null仍然是导致迷惑不清、奇怪bug和表义模糊的主要原因之一。还是上面的例子,当Map.get返回null,可能说明不存在对应的值,也可能说明值存在且为null。最大的问题在于,null无法提示这个null值表示什么意思。
因此,很多Guava utility设计为对null值直接报错;只要有不用null的代替方案,就不允许null值。另外,Guava提供了一些工具来帮你避免使用null,同时在你不得不用时让使用null更容易。
具体情况
如果你想把null加进Set中,或者作为Map的key——那就不要这么做。如果你根据情况显式地替换掉null,查找结果的含义会更加清晰。
如果你想把null用作Map的value——那就不要添加对应的entry,而要把key存进用另外一个专门的Set里。到底是Map中对应这个key的value为null,还是Map没有这个key的entry,这两种情况非常容易弄混。把值为null和不为null的这两种key分开存就要好得多了,在跟value相关联的key是null的情况下更是如此。
如果你想把null加进 List 里——如果list比较稀疏,也许用 Map<Integer, E> 更合适?这样可能效率更高,可能实际上更符合程序的需求。
设想一下如果有一个现成的"null object"可供使用。一般是没有的,但也有例外。比如,向一个 enum 加入常量,就代表了null的含义。再比如,java.math.RoundingMode 有个 UNNECESSARY 值来表示“不要取整,如果需要取整就抛出异常”。
如果你真的需要null值,使用不支持null的collection实现有困难,那只能换用另一种collection实现。例如,用 Collections.unmodifiableList(Lists.newArrayList()) 替换 ImmutableList 。
Optional类
很多情况下程序员会用 null 值来代表某种缺失:也许这里本来应该有个值,但现在没有,或是找不到。例如,Map.get 返回 null 值表示找不到这个key对应的value。
Optional<T> 是一种用非null值来替换可null的 T 引用的解决方案。一个 Optional 类要么包含一个非null的 T 引用(这种情况称为引用“存在(present)”),要么什么也不包含(这种情况称为引用“缺失(absent)”)。Optional 类从来不会“包含null”。
Optional<Integer> possible = Optional.of(5); possible.isPresent();// returns true possible.get();// returns 5
Optional 类并非与其他语言中的"option"或"maybe"结构完全等同,尽管可能有些相似性。
我们在此列出了一些常见的 Optional 类操作。
创建Optional
以下均为 Optional 类的静态方法:
Optional.of(T) | 创建一个Optional实例,包含非null的给定引用T,如果T为null则直接报错。 |
Optional.absent() | 返回一个absent的Optional实例。 |
Optional.fromNullable(T) | 将有可能为null的引用T转换为Optional实例。如果T不为null则实例为present,T为null则实例为absent。 |
查询方法
以下均为具体 Optional<T> 实例上的非静态方法:
boolean isPresent() | 如果包含非null的T(为present)实例,则返回 true 。 |
T get() | 如果为present,返回包含的 T 实例;否则抛出 IllegalStateException 。 |
T or(T) | 如果为present,返回包含的 T 实例;否则返回指定的默认值。 |
T orNull() | 如果为present,返回包含的 T 实例;否则返回 null 。这个方法是 fromNullable 的逆过程。 |
Set<T> asSet() | 如果为present,返回不可变的单例(singleton) Set ,Set中包含 Optional 中包含的 T 实例;否则返回一个空的不可变set。 |
除此之外,Optional 类还提供一些更方便的工具方法;查询Javadoc了解细节。
有何意义?
使用 Optional 除了能给 null 起个名字而增强可读性,最大的好处是它的防呆性。它会强迫你主动考虑值缺失的情况,否则就不能通过编译,因为你将需要主动解包 Optional 类,针对情况处理。讨厌的null太容易让人忘记该处理的事情,尽管FindBugs对此有些帮助,我们认为它还不足以解决问题。
Optional 这个优点的一个典型体现,就在于函数返回值既有可能"存在"也有可能"缺失"时。你(及其他人)在调用一个方法other.method(a,b)时忘记返回值可能为null的可能性,比你编写这个方法时会忘记参数 a 有为null的可能性大得多。返回值类型为 Optional 就杜绝了调用者忘记处理返回值为null的可能,因为调用者需要把返回值从 Optional 中解包出来,才能编译通过。
便捷方法
只要你想把一个 null 值替换为某个默认值,就可以用Objects.firstNonNull(T, T)。这个方法的功能顾名思义,如果两个参数 T 都是null,则直接报错,抛出NullPointerException。如果你在用 Optional,还有更好的方法——例如 first.or(second)。
Strings 中有一组方法用来处理可能为null的 String 值。这些命名直白的方法具体如下:
emptyToNull(String) |
isNullOrEmpty(String) |
nullToEmpty(String) |
我们想要强调,这些方法最主要用来应对某些讨厌的API,它们把null String和空String("")等同处理。而每当你自己写出把null string和 "" 混合处理的代码,Guava团队都会哭泣。(如果这两者的含义显著不同,那还好一些。但是把它们当做完全一样的东西处理,是一种不幸很常见的代码坏味道。)
中文翻译自Guava官方文档:GuavaExplained - UsingAndAvoidingNullExplained 译者:戴仓薯