您的位置:首页 > 其它

Chapter 1 -- UsingAndAvoidingNull

2013-09-23 00:02 330 查看
"Null sucks." -Doug Lea

"Null 很恶心!"


"I call it my billion-dollar mistake." - Sir C. A. R. Hoare, on his invention of the null reference

"我称它为我的十亿美元错误"


Using and avoiding null 使用和避免null

Careless use of null can cause a staggering variety of bugs. Studying the Google code base, we found that something like 95% of collections weren't supposed to have any null values in them, and having those fail fast rather than silently accept null would have been helpful to developers.

不小心的使用null可能会造成令人吃惊的各种各样的bug.学习Google的代码基础,我们发现95%的collections不支持任何的null值,并且使用fail-fast机制而不是默默地接受null对开发者会更有帮助.

Additionally, null is unpleasantly ambiguous. It's rarely obvious what a null return value is supposed to mean -- for example, Map.get(key) can return null either because the value in the map is null, or the value is not in the map. Null can mean failure, can mean success, can mean almost anything. Using something other than null makes your meaning clear.

另外, null是非常有歧义的的一个东西.很难明确一个null返回值表示的是什么意思 -- 例如, Map.get(key) 会返回 null 可能因为value在map里是null, 也有可能是value根本不在map里. null可以代表事变,也可以代表成功,可以代表任何意思. 使用其他东西来代替null会是你的表意更清晰.

That said, there are times when null is the right and correct thing to use. null is cheap, in terms of memory and speed, and it's unavoidable in object arrays. But in application code, as opposed to libraries, it is a major source of confusion, difficult and weird bugs, and unpleasant ambiguities -- e.g. when Map.get returns null, it can mean the value was absent, or the value was present and null. Most critically, null gives no indication what a null value means.

即便如此,有时候null还是有必要使用的.null在内存和速度方面成本低,并且在对象数组中是不可避免的.但是在程序代码中,与类库截然相反,他确实造成迷惑, bug 和 歧义的主要原因 -- 例如, 当Map.get 返回null, 他可能表示value缺失, 也有可能表示 value就是null.最重要的是, null没有任何迹象可以表明一个null值是什么意思.

For these reasons, many of Guava's utilities are designed to fail fast in the presence of null rather than allow nulls to be used, so long as there is a null-friendly workaround available. Additionally, Guava provides a number of facilities both to make using null easier, when you must, and to help you avoid using null.

由于这些原因,许多Guava的工具类只要有null-friendly的解决方案, 就设计成了fail-fast机制而不是直接使用null.另外,Guava在你必须使用null的时候提供了一系列的工具来使null用起来更简单并帮助你避免使用null.

Specific Cases 特殊类

If you're trying to use null values in a Set or as a key in a Map -- don't; it's clearer (less surprising) if you explicitly special-case null during lookup operations.

假如你想在set中使用null值或者把null作为map的key -- 别这么做; 如果你在查询操作中明确使用null的特殊情况会跟清晰.

If you want to use null as a value in a Map -- leave out that entry; keep a separate Set of non-null keys (or null keys). It's very easy to mix up the cases where a Map contains an entry for a key, with value null, and the case where the Map has no entry for a key. It's much better just to keep such keys separate, and to think about what it means to your application when the value associated with a key is null.

如果你想在map中使用null -- 别考虑entry; 保持一个分离的non-null key的set(或者null key). 一个map包含一个null的key和一个map不包含这个key这两种情况是非常容易弄混的. 最好是保持这样的keys被隔离, 考虑清楚你的key是null到底表示什么意思.

If you're using nulls in a List -- if the list is sparse, might you rather use a Map<Integer, E>? This might actually be more efficient, and could potentially actually match your application's needs more accurately.

假如你在List中使用null -- 假如这个list是稀疏的, 你最好用一个 Map<Integer, E> 这样会更高效, 而且能更精准地匹配你的程序的需求.

Consider if there is a natural "null object" that can be used. There isn't always. But sometimes. For example, if it's an enum, add a constant to mean whatever you're expecting null to mean here. For example, java.math.RoundingMode has an UNNECESSARY value to indicate "do no rounding, and throw an exception if rounding would be necessary."

想象一下有一个自然的"null object"可能被使用. 例如, 假如是个enum, 添加一个常量来表示你想表示的null.例如, java.math.RoundingMode 有一个 UNNECESSARY 值用来表示 "不要四舍五入, 并且如果需要四舍五入会抛出一个异常"

If you really need null values, and you're having problems with a null-hostile collection implementations, use a different implementation. For example, use Collections.unmodifiableList(Lists.newArrayList()) instead of ImmutableList.

如果你真得需要null, 你会在null-hostile(null不友好)的集合实现遇到问题, 所以使用一个不一样的的实现吧. 例如, 使用 Collections.unmodifiableList(Lists.newArrayList())替代ImmutableList

Optional

Many of the cases where programmers use null is to indicate some sort of absence: perhaps where there might have been a value, there is none, or one could not be found. For example, Map.get returns null when no value is found for a key.

在许多情况下程序使用null是为了表示某种意义上的absence(缺失): 可能有一个值,但他是空的,也有可能这个值未找到. 例如, Map.get 当没有找到这个key的时候会返回 null.

Optional<T> is a way of replacing a nullable T reference with a non-null value. An Optional may either contain a non-null T reference (in which case we say the reference is "present"), or it may contain nothing (in which case we say the reference is "absent"). It is never said to "contain null."

Optional<T> 是一种使用non-null value替代nullable T 引用的方法. 一个 Optional 可能包含一个non-null T 引用(这种情况我们称reference是"present"(存在的)), 或者可能什么也不包含(这种情况我们称reference是"absent"(缺失的)).我们从不会说"包含 null".

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

Optional is not intended as a direct analogue of any existing "option" or "maybe" construct from other programming environments, though it may bear some similarities.

Optional 不打算直接模拟其他编程环境中的已存在的任何"option"或"maybe",虽然他们存在一些共同点.

We list some of the most common Optional operations here.

下面列举了一些最常用的Optional操作

Making an Optional 创建一个Optional

Each of these are static methods on Optional.

Optional.of(T)Make an Optional containing the given non-null value, or fail fast on null.

创建一个包含给定非null值的Optional,如果给定值是null则使用fail fast机制抛出异常

Optional.absent()Return an absent Optional of some type.

返回某个类型的absent Optional

Optional.fromNullable(T)Turn the given possibly-null reference into an Optional, treating non-null as present and null as absent.

将一个可能为null的refernce放入Optional, 将非null当做present, null当做absent

Query methods 查询方法

Each of these are non-static methods on a particular Optional<T> value.

这些方法都是特定的 Optional<T> 实例的非静态方法

boolean isPresent()Returns true if this Optional contains a non-null instance.

返回 true 如果这个Optional包含一个non-null实例

T get()Returns the contained T instance, which must be present; otherwise, throws an IllegalStateException.

返回包含的T实例, T必须是present的;否则,抛出 IllegalStateException

T or(T)Returns the present value in this Optional, or if there is none, returns the specified default.

返回Optional中的present值, 如果这个值不存在,则返回指定的默认值

如果是 Absent 实例, or(Object defaultValue) 返回 defaultValue

如果是 Present 实例, or(T defaultValue) 返回 present value

T orNull()Returns the present value in this Optional, or if there is none, returns null. The inverse operation of fromNullable.

返回 present value, 如果没有 present value, 则返回 null, 这是 fromNullable 的逆向操作

Set<T> asSet()Returns an immutable singleton Set containing the instance in this Optional, if there is one, or otherwise an empty immutable set.

如果存在present value则返回一个不可变的单例的包含这个Optional的Set, 否则返回一个空的 immutable set

Optional provides several more handy utility methods besides these; consult the Javadoc for details.

Optional 提供了更多便利工具方法; 详情请查阅 Javadoc

What's the point? 这么做的意义是什么?

Besides the increase in readability that comes from giving null a name, the biggest advantage of Optional is its idiot-proof-ness. It forces you to actively think about the absent case if you want your program to compile at all, since you have to actively unwrap the Optional and address that case. Null makes it disturbingly easy to simply forget things, and though FindBugs helps, we don't think it addresses the issue nearly as well.

给予null一个名字除了提高可读性, Optional 最大的优势是它的"防误操作性"(idiot-proof-ness).如果你想你的程序编译通过,他强制你主动的去思考absent的情况, 因为你需要主动去展开Optional并解决absent的情况. Null使得它非常容易忽略一些事情, 即使FindBugs可以帮助我们,我们也认为它对解决这个问题有很好的效果.

This is especially relevant when you're returning values that may or may not be "present." You (and others) are far more likely to forget thatother.method(a, b) could return a null value than you're likely to forget that a could be null when you're implementing other.method. ReturningOptional makes it impossible for callers to forget that case, since they have to unwrap the object themselves for their code to compile.

这跟你的返回值是或不是"present"是息息相关的.你更大的可能是忘了 other.method(a,b) 会返回一个null值,而不是当你实现other.method的时候忘记 a 可能是null. 返回 Optional 使调用者不可能忘记返回值是null这种情况,因为他们必须展开这个Optional才能让代码通过编译

Convenience methods 便利方法

Whenever you want a null value to be replaced with some default value instead, use Objects.firstNonNull(T, T). As the method name suggests, if both of the inputs are null, it fails fast with a NullPointerException. If you are using an Optional, there are better alternatives -- e.g. first.or(second).

无论什么时候你想用一些默认值代替一个null值, 请使用 Objects.firstNonNull(T, T). 正如这个方法的名字建议的, 加入两个参数都是null, 他会进入fails fast机制抛出NullPointerException. 如果你使用Optional,则有更好的替代方法 -- 例如, first.or(second).

A couple of methods dealing with possibly-null String values are provided in Strings. Specifically, we provide the aptly named:

Strings提供了几个处理可能为null的String的方法. 我们专门为他们提供了合适的命名

emptyToNull(String)
isNullOrEmpty(String)
nullToEmpty(String)
We would like to emphasize that these methods are primarily for interfacing with unpleasant APIs that equate null strings and empty strings. Every time you write code that conflates null strings and empty strings, the Guava team weeps. (If null strings and empty strings mean actively different things, that's better, but treating them as the same thing is a disturbingly common code smell.)

强调一下,这些方法主要是用来替代那些令人不爽的吧null和""的String当做一样的东西的APIs的.每次你写代码吧null和"" String结合在一起,Guava团队都在哭泣.(如果null和"" String代表着完全不同的东西,那样还好,但是把他们看成是一样的东西这绝对还是普遍存在的代码问题)

最后是UserGuide里提到的方法示例

public static void main(String[] args) {
// Present test
System.out.println("--- Present test ---");
Optional<String> present = Optional.of("Present Val");
System.out.println(present.get());
System.out.println(present.isPresent());
System.out.println(present.or("Default Value"));
System.out.println(present.orNull());

System.out.println();

// Absent test
System.out.println("--- Absent test ---");
Optional<String> absent = Optional.absent();
// throw IllegalStateException
// System.out.println(absent.get());
System.out.println(absent.isPresent());
System.out.println(absent.or("Default Value"));
System.out.println(absent.orNull());

System.out.println();

// dynamic Absent or Present Optional
System.out.println("--- dynamic Absent or Present Optional ---");
Optional<String> dynamic1 = Optional.fromNullable("None-Null"); // equals to Option.of("None-Null")
Optional<String> dynamic2 = Optional.fromNullable(null); // equals to Option.absent()
System.out.println(dynamic1.isPresent());
System.out.println(dynamic2.isPresent());

System.out.println();

// Objects.firstNonNull() test
System.out.println("--- Objects.firstNonNull() test ---");
System.out.println(Objects.firstNonNull(null, "None-Null"));

System.out.println();

// Strings convenience method test
System.out.println("--- Strings convenience method test ---");
String[] strArr = { null, "", "None-Null" };
for (String str : strArr) {
System.out.println(Strings.emptyToNull(str));
System.out.println(Strings.isNullOrEmpty(str));
System.out.println(Strings.nullToEmpty(str));
}
}


Output

--- Present test ---
Present Val
true
Present Val
Present Val

--- Absent test ---
false
Default Value
null

--- dynamic Absent or Present Optional ---
true
false

--- Objects.firstNonNull() test ---
None-Null

--- Strings convenience method test ---
null
true

null
true

None-Null
false
None-Null
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: