在阅读Optional的源码时看到很多地方都使用Object.requireNonNull()方法,于是好奇的查了一下这个到底具体作用是什么
案例如:
public <U> Optional<U> map(Function<? super T, ? extends U> mapper) {
Objects.requireNonNull(mapper);
if (!isPresent()) {
return empty();
} else {
return Optional.ofNullable(mapper.apply(value));
}
}
public Optional<T> or(Supplier<? extends Optional<? extends T>> supplier) {
Objects.requireNonNull(supplier);
if (isPresent()) {
return this;
} else {
@SuppressWarnings("unchecked")
Optional<T> r = (Optional<T>) supplier.get();
return Objects.requireNonNull(r);
}
}
其实就是判断传递过来的参数是否为空,如果为空的话就抛出空指针异常
以下是该方法的源码
public static <T> T requireNonNull(T obj) {
if (obj == null)
throw new NullPointerException();
return obj;
}
这看上去好像是一个多余的操作, 因为如果我们试图去调用一个空对象的方法, 也会抛出 NullPointerException 运行时异常, 那么我们为什么要多此一举进行这样的一次检查呢? 这一问题在 StackOverflow 上有人进行了解答 Why should one use Objects.requireNonNull?
看了他们的回答, 总结为以下几点:
首先, 从这个方法的名称可以看出, 这个方法使用的场景是, 我们使用一个对象的方法时, 正常的运行状态应该能保证这个对象的引用非空, 如果这个对象为空了, 那一定是其他某个地方出错了, 所以我们应该抛出一个异常, 我们不应该在这里处理这个非空异常.
其次, 这里涉及到一个很重要的编程思想, 就是 Fail-fast 思想, 翻译过来就是, 让错误尽可能早的出现, 不要等到我们很多工作执行到一半之后才抛出异常, 这样很可能使得一部分变量处于异常状态, 出现更多的错误. 这也是 requireNonNull 这个方法的设计思想, 让错误尽早出现. 使用这个方法, 我们明确的抛出异常, 发生错误时, 我们立刻抛出异常.
除此之外, 这个方法的作用也是一个明确和不明确的区别, 使用这个方法表示我们明确进行了这个判断, 其实与我们自己使用 if-else 进行判断是一样的, 只是这个工具类简化了这样的操作, 让我们的代码看上去更加简洁, 可读性更强.
此外, requireNonNull 方法有一个重载方法, 可以提供一个报错信息, 以供我们 debug 的时候显示. 我们使用这个引用的时候, 应当保证非空, 如果不然, 会抛出异常告诉我们其他地方出错了, 这里出现了空指针异常