规避 NullPointException 的一些策略

NullPointException 是 Android 开发中最常见,也最容易 fix 的异常之一。每当发生 NullPointException 时,解决方案大多都是根据错误堆栈,获取错误行数,然后加个 if (someObject != null) 的判断。看似简单的 fix,实际开发中,我们也不可能会对每个需要使用的 Object,进行非空判断,这样会导致代码十分冗余,而且,这样做,有些非空判断,可能永远都是返回 true. 除了给代码添加行数外,也没什么实际意义。

所以,问题的关键是,要辨别对象是否可能为空,然后给可能为空的变量进行非空判断。下面总结了几种策略来帮我们处理辨别对象是否为空的情况。

1、@Nullable

@Nullable 是 Java的一个注解,可以对成员变量,方法,以及方法参数进行修饰。分别对应成员变量、方法返回值、方法参数可能为空。调用者在使用这些变量、方法时,应该进行非空判断。借助 Android Studio IDE,如果使用前没有进行非空判断,IDE 会有一个 warning,提醒进行非空判断。相应的还有一个 @Nonnull 注解,修饰范围和 @Nullable 一样,标记变量或者返回值不可能为空,调用方无需进行非空判断。

2、命名约定

通过命名约定来变量、返回值可能为空的情况,比如,nullableDate,nullableString, getNullableVersionName(),调用方根据方法名、变量名,判断是否需要非空判断。

3、Optional\

Optional\ 使用一个 Optional 泛型包装一个可能为空的对象,当调用方发现当前类型为 Optional 时,应该进行一下 Optional 对应的 value 是否为空的判断,然后进行后续操作。Optional 可以看做是一种强制进行非空判断的策略。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class Optional<T> {
private T value;
private boolean isChecked;

private Optional(T value) {
this.value = value;
}

public static <T> Optional<T> create(T t) {
return new Optional<>(t);
}

public static <T> Optional<T> nullValue() {
return new Optional<>(null);
}

public T get() {
assert isChecked : "You must call isPresent() first before get()";
return value;
}

public boolean isPresent() {
isChecked = true;
return value != null;
}
}

4、 防御性编程

其实上面任何一条都不是完美的,因为我们无法做到整个项目都遵循这些策略,毕竟我们的项目底层依赖 SDK,还引用了大量第三方 framework,简称别人的代码,这些别人的代码,是如何处理非空问题的,大多都没有遵循以上任意一条,我们也就无法通过一些约定来进行处理判断。此时,不要相信任何外界输入输出。在使用第三方方法、网络数据时,多个心眼,多加判断,往往是利大于弊。

我们在写 library 时,要考虑别人传来的参数的可能情况,做好防御。给别人返回值时,集合类返回 emptyList/emptyMap,数组返回 0 长数组。