我已经注意到Oracle JDK中的许多Java 8方法使用Objects.requireNonNull(),如果给定的对象(参数)为空,它会在内部抛出NullPointerException。

public static <T> T requireNonNull(T obj) {
    if (obj == null)
        throw new NullPointerException();
    return obj;
}

但是如果空对象被解引用,无论如何都会抛出NullPointerException。那么,为什么要做这个额外的空检查并抛出 NullPointerException吗?

一个明显的答案(或好处)是它使代码更具可读性,我同意这一点。我很想知道使用的其他原因 方法开头的Objects.requireNonNull()。


当前回答

因为这样做可以使事情显式化。如:

public class Foo {
  private final Bar bar;

  public Foo(Bar bar) {
    Objects.requireNonNull(bar, "bar must not be null");
    this.bar = bar;
  }

或更短:

  this.bar = Objects.requireNonNull(bar, "bar must not be null");

现在你知道了:

当使用new()成功创建Foo对象时 那么它的bar字段保证是非空的。

与此相比:今天创建了一个Foo对象,明天调用一个使用该字段并抛出的方法。最有可能的是,明天您将不知道为什么该引用昨天传递给构造函数时为空!

换句话说:通过显式地使用此方法检查传入引用,您可以控制抛出异常的时间点。大多数时候,你都想尽快失败!

主要优点是:

如前所述,控制行为 更容易调试-因为在对象创建的上下文中抛出。在某个时间点,您的日志/跟踪有一定的机会告诉您哪里出了问题! 如上所示:这个想法的真正力量与最终场相结合。因为现在类中的任何其他代码都可以安全地假设bar不为空——因此您不需要在其他地方进行任何if (bar == null)检查!

其他回答

还有一个额外的好处,静态分析工具通常知道标准库的@NonNull和@Nullable(这与显式检查null相同):

public static <T> @NonNull T requireNonNull(@Nullable T obj) {
    if (obj == null)
        throw new NullPointerException();
    return obj;
}

NPE(空指针异常)是在你访问一个对象的成员时抛出的。Objects.requireNonNull()从访问值的位置回溯到初始化为null的位置,因此可以关注NPE的真正来源。

一个最简单的场景:

// Global String
String nullString = null;

调用空值:

public void useString() {
    nullString.length();  // The source of exception, without using Objects.requireNonNull()
}

现在如果我们在初始化时使用Objects.requireNonNull():

// Global String
String nullString = Objects.requireNonNull(null); // The source of exception which is indeed the actual source of NPE

作为旁注,在object# requireNotNull之前,在java-9之前的一些jre类中实现的这个失败速度略有不同。假设如下情况:

 Consumer<String> consumer = System.out::println;

在java-8中,编译为(仅相关部分)

getstatic Field java/lang/System.out
invokevirtual java/lang/Object.getClass

基本上是一个操作:yourReference。getClass -如果你的引用为空,它将失败。

在jdk-9中发生了变化,相同的代码编译为

getstatic Field java/lang/System.out
invokestatic java/util/Objects.requireNonNull

或者基本上是对象。requireNotNull(一份)

我认为它应该用于复制构造函数和其他一些情况,如DI,其输入参数是一个对象,你应该检查参数是否为空。 在这种情况下,您可以方便地使用此静态方法。

除了其他答案-对我来说,使用requireNonNull可以使代码更方便,(有时容易阅读)

例如,让我们检查下面的代码,

private int calculateStringLength(String input) {
        return Objects.
                requireNonNull(input, "input cannot be null").
                length();
    }

这段代码返回传递给它的字符串长度作为参数——但是如果输入为空,它将抛出一个NPE。

正如您所看到的,使用requireNonNull -没有理由再手动执行空检查了。

另一个有用的东西是“异常消息”是我自己手写的(在这种情况下输入不能为空)。