Java 为什么要使用 Objects.requireNonNull()?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/45632920/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 02:05:50  来源:igfitidea点击:

Why should one use Objects.requireNonNull()?

javajava-8nullpointerexception

提问by user4686046

I have noted that many Java 8 methods in Oracle JDK use Objects.requireNonNull(), which internally throws NullPointerExceptionif the given object (argument) is null.

我注意到 Oracle JDK 中的许多 Java 8 方法都使用Objects.requireNonNull()NullPointerException如果给定的对象(参数)是null.

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

But NullPointerExceptionwill be thrown anyway if a nullobject is dereferenced. So, why should one do this extra null check and throw NullPointerException?

但是NullPointerException如果null对象被取消引用,无论如何都会抛出。那么,为什么要进行这种额外的空检查并抛出 NullPointerException

One obvious answer (or benefit) is that it makes code more readable and I agree. I'm keen to know any other reasons for using Objects.requireNonNull()in the beginning of the method.

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

采纳答案by GhostCat

Because you can make things explicitby doing so. Like:

因为你可以通过这样做使事情变得明确。喜欢:

public class Foo {
  private final Bar bar;

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

Or shorter:

或更短:

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

Now you know:

现在你知道了

  • whena Foo object was successfully created using new()
  • thenits barfield is guaranteedbe non-null.
  • 使用成功创建 Foo 对象时new()
  • 那么它的bar字段保证为非空。

Compare that to: you create a Foo object today, and tomorrowyou invoke a method that uses that field and throws. Most likely, you will not know tomorrow why that reference was null yesterdaywhen it got passed to the constructor!

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

In other words: by explicitly using this method to check incomingreferences you can controlthe point in time when the exception will be thrown. And most of the time, you want to fail as fast as possible!

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

The major advantages are:

主要优点是:

  • as said, controlledbehavior
  • easier debugging - because you throw up in the context of the object creation. At a point in time where you have a certain chance that your logs/traces tell you what went wrong!
  • and as shown above: the true power of this idea unfolds in conjunction with finalfields. Because now any other codein your class can safely assume that barisn't null - and thus you do not need any if (bar == null)checks in other places!
  • 如上所述,受控行为
  • 更容易调试 - 因为你在对象创建的上下文中抛出。在某个时间点,您有一定的机会,您的日志/跟踪会告诉您出了什么问题!
  • 如上所示:这个想法的真正力量与最终领域一起展开。因为现在您班级中的任何其他代码都可以安全地假设它bar不为空 - 因此您不需要if (bar == null)在其他地方进行任何检查!

回答by luk2302

Fail-fast

快速失败

The code should crash as soon as possible. It should not do half of the work and then dereference the null and crash only then leaving half of some work done causing the system to be in an invalid state.

代码应该尽快崩溃。它不应该完成一半的工作,然后取消引用 null 并崩溃,然后才完成一半的工作,导致系统处于无效状态。

This is commonly called "fail early" or "fail-fast".

这通常称为“早期失败”或“快速失败”

回答by Jon Skeet

But NullPointerException will be thrown anyway if a null object is dereferenced. So, why should one do this extra null check and throw NullPointerException?

但是,如果取消引用空对象,则无论如何都会抛出 NullPointerException。那么,为什么要进行这种额外的空检查并抛出 NullPointerException 呢?

It means you detect the problem immediatelyand reliably.

这意味着你发现问题,立即可靠

Consider:

考虑:

  • The reference may not be used until later in the method, after your code has already performed some side-effects
  • The reference may not be dereferenced in this method at all
    • It could be passed to completely different code (i.e. cause and error are far apart in code space)
    • It could be used much later (i.e. cause and error are far apart in time)
  • It may be used somewhere that a null reference isvalid, but has an unintended effect
  • 在您的代码已经执行了一些副作用之后,该引用可能不会在该方法的后面使用
  • 在此方法中可能根本不会取消引用引用
    • 它可以传递给完全不同的代码(即原因和错误在代码空间中相距甚远)
    • 它可以在很久以后使用(即原因和错误在时间上相距甚远)
  • 它可用于地方,一个空引用有效的,但是有一个意想不到的效果

.NET makes this better by separating NullReferenceException("you dereferenced a null value") from ArgumentNullException("you shouldn't have passed in null as an argument - and it was for thisparameter). I wish Java did the same thing, but even with just a NullPointerException, it's still mucheasier to fix code if the error is thrown at the earliest point at which it can be detected.

.NET 通过将NullReferenceException(“您取消引用一个空值”)与ArgumentNullException(“您不应该将 null 作为参数传入 - 而它是用于参数的)使这变得更好。我希望 Java 也能做同样的事情,但即使使用只是一个NullPointerException,如果错误是在可以检测到的最早点抛出,那么修复代码仍然容易得多

回答by davidxxx

Using requireNonNull()as first statements in a method allow to identify right now/fast the cause of the exception.
The stacktrace indicates clearly that the exception was thrown as soon as the entry of the method because the caller didn't respect the requirements/contract.Passing a nullobject to another method mayindeed provoke an exception at a time but the cause of the problem may be more complicated to understand as the exception will be thrown in a specific invocation on the nullobject that may be much further.

requireNonNull()在方法中使用as first statements 允许立即/快速识别异常的原因。
堆栈跟踪清楚地表明异常在方法一进入就被抛出,因为调用者没有遵守要求/合同。将一个null对象传递给另一个方法确实可能一次引发异常,但问题的原因可能更难以理解,因为异常将在null对象上的特定调用中抛出,可能会更远。



Here is a concrete and real example that shows why we have to favor fail fast in general and more particularly using Object.requireNonNull()or any way to perform a no null check on parameters designed to be not null.

这是一个具体而真实的例子,它说明了为什么我们必须在一般情况下支持快速失败,更具体地说,使用Object.requireNonNull()或任何方式对设计为 not 的参数执行非空检查null

Suppose a Dictionaryclass that composes a LookupServiceand a Listof Stringrepresenting words contained in. These fields are designed to be not nulland one of these is passed in the Dictionaryconstructor.

假设一个Dictionary类由 aLookupService和 a组成ListString表示其中包含的单词。这些字段被设计为 notnull并且其中一个在Dictionary构造函数中传递 。

Now suppose a "bad" implementation of Dictionarywithout nullcheck in the method entry (here that is the constructor):

现在假设在方法条目中Dictionary没有null检查的“坏”实现(这里是构造函数):

public class Dictionary {

    private final List<String> words;
    private final LookupService lookupService;

    public Dictionary(List<String> words) {
        this.words = this.words;
        this.lookupService = new LookupService(words);
    }

    public boolean isFirstElement(String userData) {
        return lookupService.isFirstElement(userData);
    }        
}


public class LookupService {

    List<String> words;

    public LookupService(List<String> words) {
        this.words = words;
    }

    public boolean isFirstElement(String userData) {
        return words.get(0).contains(userData);
    }
}

Now, let's invoke the Dictionaryconstructor with a nullreference for the wordsparameter :

现在,让我们Dictionary使用参数的null引用来调用构造words函数:

Dictionary dictionary = new Dictionary(null); 

// exception thrown lately : only in the next statement
boolean isFirstElement = dictionary.isFirstElement("anyThing");

The JVM throws the NPE at this statement :

JVM 在此语句中抛出 NPE:

return words.get(0).contains(userData); 
Exception in thread "main" java.lang.NullPointerException
    at LookupService.isFirstElement(LookupService.java:5)
    at Dictionary.isFirstElement(Dictionary.java:15)
    at Dictionary.main(Dictionary.java:22)

The exception is triggered in the LookupServiceclass while the origin of it is well earlier (the Dictionaryconstructor). It makes the overall issue analysis much less obvious.
Is wordsnull? Is words.get(0) null? Both ? Why the one, the other or maybe both are null? Is it a coding error in Dictionary(constructor? invoked method?) ? Is it a coding error in LookupService? (constructor? invoked method?) ?
Finally, we will have to inspect more code to find the error origin and in a more complex class maybe even use a debugger to understand more easily what it happened.
But why a simple thing (a lack of null check) become a complex issue ?
Because we allowed the initial bug/lack identifiable on a specific component leak on lower components.
Imagine that LookupServicewas not a local service but a remote service or a third party library with few debugging information or imagine that you didn't have 2 layers but 4 or 5 layers of object invocations before that the nullbe detected ? The problem would be still more complex to analyse.

异常是在LookupService类中触发的,而它的起源要早得多(Dictionary构造函数)。它使整体问题分析变得不那么明显。
wordsnull吗?是words.get(0) null吗?两个都 ?为什么一个,另一个或者两者都是null?它是Dictionary(构造函数?调用的方法?)中的编码错误吗?是编码错误LookupService吗?(构造函数?调用的方法?)?
最后,我们将不得不检查更多代码以找到错误来源,并且在更复杂的类中甚至可能使用调试器更容易地理解它发生了什么。
但是为什么一件简单的事情(缺少空检查)会变成一个复杂的问题?
因为我们允许在较低组件上的特定组件泄漏上可以识别初始错误/缺失。
想象一下,这LookupService不是本地服务,而是远程服务或具有很少调试信息的第三方库,或者想象一下,在null被检测到之前,您没有 2 层而是 4 或 5 层对象调用?这个问题分析起来会更加复杂。

So the way to favor is :

所以赞成的方法是:

public Dictionary(List<String> words) {
    this.words = Objects.requireNonNull(words);
    this.lookupService = new LookupService(words);
}

In this way, no headache : we get the exception thrown as soon as this is received :

这样,不用头疼:我们一收到就抛出异常:

// exception thrown early : in the constructor 
Dictionary dictionary = new Dictionary(null);

// we never arrive here
boolean isFirstElement = dictionary.isFirstElement("anyThing");
Exception in thread "main" java.lang.NullPointerException
    at java.util.Objects.requireNonNull(Objects.java:203)
    at com.Dictionary.(Dictionary.java:15)
    at com.Dictionary.main(Dictionary.java:24)

Note that here I illustrated the issue with a constructor but a method invocation could have the same non null check constraint.

请注意,这里我用构造函数说明了这个问题,但方法调用可能具有相同的非空检查约束。

回答by Eugene

As a side note, this fail fast before Object#requireNotNullwas implemented slightly different before java-9 inside some of the jre classes themselves. Suppose the case :

附带说明一下,Object#requireNotNull在某些 jre 类本身中,在 java-9之前实现此快速失败之前略有不同。假设情况:

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

In java-8 this compiles as (only the relevant parts)

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

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

Basically an operation as : yourReference.getClass- which would fail if yourRefercence is null.

基本上是一个操作:yourReference.getClass- 如果 yourReference 是null.

Things have changed in jdk-9 where the same code compiles as

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

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

Or basically Objects.requireNotNull (yourReference)

或者基本上 Objects.requireNotNull (yourReference)

回答by Supun Wijerathne

The basic usage is checking and throwing NullPointerExceptionimmediately.

基本用法是NullPointerException立即检查和抛出。

One better alternative (shortcut) to cater to the same requirement is @NonNullannotation by lombok.

满足相同要求的一种更好的替代方法(快捷方式)是lombok 的 @NonNull注释。

回答by Mirwise Khan

Null pointer exception is thrown when you access a member of an object which is nullat a later point. Objects.requireNonNull()immediately checks the value and throws exception instantly without moving forward.

当您访问null稍后点的对象成员时,将引发空指针异常。Objects.requireNonNull()立即检查该值并立即抛出异常而不继续前进。

回答by Yu Chai

I think it should be used in copy constructors and some other cases like DI whose input parameter is an object, you should check if the parameter is null. In such circumstances, you can use this static method conveniently.

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