是否为空参数提供IllegalArgumentException或者NullPointerException?

时间:2020-03-05 18:38:08  来源:igfitidea点击:

我为属性设置了一个简单的setter方法,而'null'不适用于此特定属性。在这种情况下,我总是很痛苦:我应该抛出IllegalArgumentException还是NullPointerException吗?从javadocs看来,两者都合适。有某种可以理解的标准吗?还是这只是我们应该做的任何事情之一,而且两者都是正确的?

解决方案

回答

如果我们不希望将null用作允许值,则似乎需要调用IllegalArgumentException;如果尝试使用一个原来为null的变量,则将抛出NullPointerException。

回答

如果它是一个" setter"方法,并且将" null"传递给它,我认为抛出" IllegalArgumentException"会更有意义。在我们尝试实际使用null的情况下,NullPointerException似乎更有意义。

因此,如果我们正在使用它,并且它是nullNullPointer。如果它被传入并且为空,则为" IllegalArgument"。

回答

我倾向于遵循JDK库的设计,尤其是Collections和Concurrency(Joshua Bloch,Doug Lea,那些人知道如何设计可靠的API)。无论如何,JDK中的许多API都会主动抛出NullPointerException

例如,Map.containsKey的Javadoc指出:

@throws NullPointerException if the key is null and this map
    does not permit null keys (optional).

抛出自己的NPE是完全有效的。约定将在异常消息中包含为空的参数名称。

模式如下:

public void someMethod(Object mustNotBeNull) {  
    if (mustNotBeNull == null) {  
        throw new NullPointerException("mustNotBeNull must not be null");  
    }  
}

无论我们做什么,都不要设置一个错误的值,并在以后其他代码尝试使用它时引发异常。这使得调试成为一场噩梦。我们应该始终遵循"快速失败"的原则。

回答

如果选择抛出NPE,并且在方法中使用了参数,则显式检查null可能是多余且昂贵的。我认为VM已经为我们做到了。

回答

如果使用IllegalArgumentException(String message)来声明参数无效并提供尽可能多的详细信息,这是公认的做法。也就是说,在异常非null的情况下,发现参数为null,我们可以执行一些操作像这样:

if( variable == null )
    throw new IllegalArgumentException("The object 'variable' cannot be null");

我们几乎没有理由隐式使用" NullPointerException"。 NullPointerException是Java虚拟机在我们尝试在空引用上执行代码时引发的异常(例如toString())。

回答

标准是抛出NullPointerException。通常可靠的"有效Java"在第42项(第一版),第60项(第二版)或者第72项(第三版)"优先使用标准异常"中对此进行了简要讨论:

"Arguably, all erroneous method
  invocations boil down to an illegal
  argument or illegal state, but other
  exceptions are standardly used for
  certain kinds of illegal arguments and
  states. If a caller passes null in
  some parameter for which null values
  are prohibited, convention dictates
  that NullPointerException be thrown
  rather than IllegalArgumentException."

回答

上面两个例外的链接中的定义是
IllegalArgumentException:抛出该异常以指示方法已传递非法或者不适当的参数。
NullPointerException:当应用程序在需要对象的情况下尝试使用null时抛出。

这里最大的区别是,应该在检查方法的参数是否有效时使用IllegalArgumentException。当对象为null时,只要"使用"对象,就应该使用NullPointerException。

我希望这有助于将两者放在一起。

回答

我完全同意所说的话。早期失败,快速失败。相当不错的异常口头禅。

有关抛出哪个异常的问题主要取决于个人喜好。在我看来,IllegalArgumentException似乎比使用NPE更具体,因为它告诉我问题出在我传递给方法的参数上,而不是执行该方法时可能生成的值。

我的2美分

回答

如果它是"设定者",或者是我以后要让某个成员使用的某个地方,那么我倾向于使用IllegalArgumentException。

如果现在要在该方法中使用(取消引用),我会主动抛出NullPointerException。与让运行时执行此操作相比,我更喜欢此操作,因为我可以提供有用的消息(似乎运行时也可以执行此操作,但这又是另一天的麻烦)。

如果我要覆盖某个方法,则将使用该覆盖方法所使用的任何东西。

回答

通常,开发人员永远不应抛出NullPointerException。当代码尝试取消引用值为空的变量时,运行时将引发此异常。因此,如果方法想要显式禁止null,而不是碰巧使null值引发NullPointerException,则应抛出IllegalArgumentException。

回答

出于以下原因,我们应该使用" IllegalArgumentException"(IAE),而不是" NullPointerException`(NPE)":

首先,NPE JavaDoc明确列出了适合使用NPE的情况。注意,当不恰当地使用null时,所有它们都会被运行时抛出。相比之下,IAE JavaDoc并不清楚:"抛出该错误以表明方法已传递了非法或者不适当的参数。"是的,就是你!

其次,当我们在堆栈跟踪中看到NPE时,我们会怎么做?可能有人取消引用了" null"。当我们看到IAE时,我们假定在堆栈顶部传递了非法值的方法的调用者。同样,后一种假设是正确的,前一种是误导性的。

第三,由于IAE显然是为验证参数而设计的,因此我们必须将其视为默认的例外选择,那么为什么要选择NPE呢?当然不是出于不同的行为-我们真的希望调用代码与IAE分开捕获NPE并因此有所作为吗?我们是否要传达更具体的错误消息?但是无论如何,我们都可以在异常消息文本中执行此操作,就像对所有其他不正确的参数一样。

第四,所有其他不正确的参数数据将是IAE,那么为什么不一致?为什么非法的" null"是如此特殊,以致于它应与所有其他类型的非法论证分开单独的例外?

最后,我接受其他答案给出的论点,即Java API的某些部分以这种方式使用NPE。但是,Java API与从异常类型到命名约定的所有内容都不一致,因此我认为仅仅盲目复制(我们最喜欢的部分)Java API不足以胜过这些其他考虑。

回答

我们应该抛出一个IllegalArgumentException,因为它将使程序员很明显他做了无效的事情。开发人员习惯于看到VM抛出的NPE,以至于任何程序员都不会立即意识到自己的错误,而是会开始四处张望,甚至更糟糕地将代码归咎于"笨拙"。

回答

这是一个"圣战"风格的问题。换句话说,这两种选择都是好的,但是人们会有自己的偏好,他们会捍卫自己的生命。

回答

在这种情况下,IllegalArgumentException使用API向用户传达明确的信息,即"不应为null"。正如其他论坛用户指出的,只要我们愿意,只要使用API​​向用户传达正确的信息,就可以使用NPE。

GaryF和tweakt删除了推荐使用NPE的"有效Java"(我发誓)参考。并且查看如何构造其他好的API是了解如何构造API的最佳方法。

另一个很好的例子是查看Spring API。例如,org.springframework.beans.BeanUtils.instantiateClass(构造函数ctor,Object [] args)具有Assert.notNull(ctor,"构造函数不得为null")行。 org.springframework.util.Assert.notNull(Object object,String message)方法检查传入的参数(对象)是否为null,如果存在,则抛出新的IllegalArgumentException(message),该异常随后被组织捕获。 springframework.beans.BeanUtils.instantiateClass(...)方法。

回答

我想从其他非法参数中选出Null参数,因此我从IAE派生了一个名为NullArgumentException的异常。甚至不需要读取异常消息,我知道将null参数传递到方法中,并且通过读取消息,我可以找出哪个参数为null。我仍然使用IAE处理程序捕获NullArgumentException,但是在我的日志中可以快速看到差异。

回答

Apache Commons Lang有一个NullArgumentException,它可以完成此处讨论的许多事情:它扩展了IllegalArgumentException,它的唯一构造方法采用了应该为非null的参数名称。

虽然我觉得抛出类似NullArgumentException或者IllegalArgumentException之类的东西可以更准确地描述特殊情况,但我和我的同事还是选择遵从Bloch关于该主题的建议。

回答

投票支持杰森·科恩(Jason Cohen)的论点,因为它的陈述很充分。让我逐步解散它。 ;-)

  • NPE JavaDoc明确表示"其他非法使用null对象的行为"。如果仅将其限制在运行时不应该遇到null的情况下,则可以更简洁地定义所有此类情况。
  • 如果我们假设做错了事,那将无济于事,但假设正确应用了封装,我们真的不应该在意或者注意到是否对空值进行了不适当的引用,而不是方法是否检测到不适当的空值并引发了异常。
  • 错误允许空值的逻辑与错误允许非法值的逻辑有很大的不同。例如,如果我正在验证用户输入的数据,如果我获得的值是不可接受的,则该错误的根源在于应用程序的最终用户。如果我得到一个空值,那是程序员错误。
  • 无效值可能导致诸如堆栈溢出,内存不足错误,解析异常等问题。实际上,大多数错误通常在某些时候在某些方法调用中以无效值存在。因此,我认为IAE实际上是RuntimeException下所有异常的最通用。
  • 实际上,其他无效参数可能导致各种其他异常。 UnknownHostException,FileNotFoundException,各种语法错误异常,IndexOutOfBoundsException,身份验证失败等,等等。

总的来说,我感到NPE的弊端很大,因为传统上将NPE与无法遵循快速失败原则的代码相关联。那,加上JDK无法用消息字符串填充NPE,确实造成了强烈的负面情绪,这是没有根据的。实际上,从运行时角度来看,NPE和IAE之间的区别严格来说就是名称。从这个角度来看,名称越精确,我们给呼叫者的清晰度就越高。