关于null:我应该显式抛出NullPointerException还是让Java为我做?

Should I throw a NullPointerException explicitly or let Java do it for me?

正如标题所说,我想知道关于抛出nullpointerExceptions的最佳实践是什么。具体来说,如果我有一个外部库函数,可以在我不想实际处理的情况下返回null(具体示例见下文),因为null表示软件有问题。问题是,我应该

  • 检查null的返回值,自己抛出nullpointerException,或者
  • 我是否应该让Java在我尝试使用这个对象的时候为我做那些肮脏的工作。
  • 第一种方法允许我添加一些额外的信息,因为我可以构造NullPointerException,但是第二种方法在我看来可以使代码更干净。我也想知道任何性能的影响,也就是说,Java更有效地投掷NPE"原生"?

    举个例子,我尝试使用Java语音API来创建一个语音合成器,使用以下代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    synthesizer = Central.createSynthesizer(generalDesc);

    if (synthesizer == null) {
        // (1) throw NPE explicitly
        throw new NullPointerException("No general domain synthesizer found.");
    }

    // (2) let the JVM throw the NPE when dereferencing
    synthesizer.allocate();

    如果找不到合适的合成器,Central.createSynthesizer返回null,这通常是由于缺少speech.properties文件造成的。所以这是一个系统设置错误的问题,并且在运行时是无法恢复的,而不是需要通过编程处理的情况。因此,我认为抛出NullPointerException是一个有效的响应,因为它指示一个错误(不在代码中,而是在软件的部署中)。但是,由于下一个语句中取消了synthesizer对象的引用,我应该让JVM为我抛出NPE并保存空检查吗?

    附录:考虑当JVM启动需要在文件系统上(通常)"用户.home"或"java.home /LIB"中加载时,令人困惑的是,EDCOX1 OR 6不直接抛出NPE(这是我最初在弗洛伊德滑动中写的),但是它返回NULL,而不是返回NULL。.I认为抛出NullPointerException是正确的操作,因为它指示软件部署中的实际错误。


    在你的情况下:两者都没有。检查null并抛出更有意义的异常,而不是NPE。

    一般来说,如果NPE不应该发生,不要明确地测试它,Java会为你做。编写的测试更少,读取的代码更少,分析的复杂性更低。

    但是,如果预计null会尽快进行测试并作出相应解释。否则,NullPointerException将在稍后的某个地方以不同的行/方法发生,这使得调试实际问题变得更加困难。


    我想说的是,您不应该显式地创建NullPointerException,而是使用更清楚地描述情况的异常类型。在您的例子中,我认为IllegalStateException适合"系统设置错误,运行时无法恢复"的情况。或者您可以创建自己的ComponentMissingException。在所需方法参数为null的情况下,通常使用IllegalArgumentException


    我不喜欢空值是一个有效的返回值,即使在"特殊情况"下也是如此。所以我采取了另一种方法。

    在您的例子中,我将用@notnull(@notnull是一个令人惊奇的注释)注释方法createSynthester(…)。当CreateSynthester(…)想要返回空值时,我将得到一个非法状态异常,而不是NPE。

    你会得到A:

    1
    java.lang.IllegalStateException: @NotNull method .../.../createSynthetiser(...) must not return null

    这种方法有几个好处:

    • NullPointerException和IllegalstateException都扩展了RuntimeException,因此您不会从根本上更改程序。

    • 如果发生错误,应立即抛出异常(不迟于以后,当您检查/抛出自己或尝试取消引用空值时)

    • 你不必费心写如果…==空/抛出部分。

    作为一个,巨大的,额外的好处,一些IDE(如intellij-ide a)将实时警告您可能的@notnull违规行为。


    关于原始代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    synthesizer = Central.createSynthesizer(generalDesc);

    if (synthesizer == null) {
        // (1) throw NPE explicitly
        throw new NullPointerException("No general domain synthesizer found.");
    }

    // (2) let the JVM throw the NPE when dereferencing
    synthesizer.allocate();

    我的看法是,像这里所显示的那样抛出NPE是可以的,但有巨大的警告。只有当npe-ctor参数是一个足够描述性的(并且希望是唯一的)消息(希望从常量或资源集中提取一个)时,才需要注意这个问题。另一个需要注意的是,您的主要优先权是把事情弄出去,这样的情况a,这将被视为一个可接受的快速n脏解决方案。

    在理想情况下,我的偏好是使用特定于无效配置的异常。当它们不可用时,可以使用NPE子类(如Apache Commons Math的NullArgumentException)或Apache Common Lang 2.x中的旧异常。这是我对NPE和IllegalArgument类型异常的立场。我不一定同意ApacheCommonalLang在使用标准JDK异常而不是更语义相关的异常方面的立场。但那只是我,我正在偏离正切…

    …所以回到最初的问题。正如我之前所说,以一种快速的N-肮脏的方式扔下这样的NPE是可以的(当你在其中一个"需要得到的SH1T出来!"!(10+1)"各种情况。

    但是请注意,这是由应用程序或系统配置问题引起的NPE,因为您正确地识别了它。也就是说,NPE不是由另一个错误条件(在本例中是配置或环境错误)的症状或影响造成的根本原因。

    Central.createSynthesizer returns null if it cannot find a suitable
    synthesizer, which is most often caused by a missing speech.properties
    file. So it's a matter of wrong setup of the system, and pretty
    unrecoverable from at runtime, rather than circumstances that need to
    be handled programmatically.

    不可恢复性不是确定的。应用程序可以有其他方法以编程方式处理这种情况。

    As such, I believe throwing a
    NullPointerException is a valid response, since it indicates a bug
    (not in the code but in the deployment of the software). But since the
    synthesizer object is dereferenced in the very next statement, should
    I just let the JVM throw the NPE for me and save the null check?

    我不会这么做的,即使是在这样的情况下。由JVM以这种方式抛出的NPE将在其中包含一个非常不具格式性的消息。通常,检查所有内容是否为空,并在遇到描述性异常时抛出一个异常(NPE或其他异常)。

    如果你能保证你得到的任何东西(例如参数)已经被检查过了,不要检查NPE(以合同方式设计)。

    Addendum: Considering that speech.properties gets loaded when the JVM
    starts needs to exist on the filesystem in (normally)"user.home" or
    "java.home/lib", it is puzzling that createSynthesizer doesn't
    straight up throw an NPE (which is what I had written originally in a
    Freudian slip) when it fails to find it but returns null instead.

    同样,这是因为对这种情况的响应是特定于应用程序的。应用程序可能会决定采用部分功能的跛行模式,而不是崩溃和燃烧到地面。如果createSynthesizer要抛出一个NPE,那么API设计器将强制应用程序设计器采用更晚的行为,或者执行"跛行"操作模式(必须使用catch/try而不是简单的if-null测试)。

    I think that throwing a NullPointerException is the right thing to do
    here, because it indicates an actual bug in the deployment of the
    software.

    再说一次,只有当NPE是一个快速的N-肮脏的解决方案,才能把事情弄出去。在这种情况下,一切正常。更好的方法是识别这是什么,配置错误。

    因此,最好具有特定于应用程序的异常,如IllegalConfigurationException或InvalidConfigurationException或IncompleteConfigurationException。我不喜欢在这种情况下使用java.lang.IllegalStateException,因为这不是由调用处于无效状态的对象引起的。由于配置无效,已达到无效状态。也许我在玩语义游戏,但在这种情况下使用非法状态例外有点棘手(我知道这只是我的主观感受)。


    我从未使用过Java,但是如果我正在使用该应用程序,我希望看到一个错误消息而不是崩溃。对我来说,nullpointerException听起来像是代码中的一个bug——我宁愿看到一条错误消息,其中包含如何正确配置程序的说明,或者至少是指向具有此类说明的网页的超链接。

    如果我是一个用户,看到一个程序以nullpointerException终止,我会针对该程序提交一个bug,或者至少对下一步该做什么感到困惑。


    很有趣的问题。

    我认为该方法应该抛出某种类型的ProblemCreatingSynthesizerException,而不是返回空值。

    我会进行空检查并抛出一个NPE或您自己的其他自定义ProblemWithSynthesizerException(sun,出于某种原因,认为这个异常是一个jvm-ish异常,并不打算由程序员使用。这就是它在一些认证教程和书籍中所说的。但是,我不买,有些人经常把我自己的NPE丢进我的图书馆)。