我有一个简单的setter方法的属性和空是不适合这个特定的属性。在这种情况下,我总是被撕裂:我应该抛出一个IllegalArgumentException,还是一个NullPointerException?从javadocs来看,两者都很合适。是否存在某种公认的标准?或者这只是其中一件事,你应该做任何你喜欢做的事情,两种都是正确的?


当前回答

你应该使用IllegalArgumentException (IAE),而不是NullPointerException (NPE),原因如下:

首先,NPE JavaDoc显式地列出了适用于NPE的情况。请注意,当不恰当地使用null时,所有这些都是由运行时抛出的。相比之下,IAE JavaDoc再清楚不过了:“抛出是为了表明一个方法被传递了一个非法或不适当的参数。”没错,就是你!

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

第三,由于IAE显然是为验证参数而设计的,因此必须假设它是默认的异常选择,那么为什么要选择NPE呢?当然不是针对不同的行为——你真的期望调用代码分别捕获NPE和IAE,并因此做一些不同的事情吗?您是否试图传达更具体的错误消息?但是无论如何,您都可以在异常消息文本中这样做,就像处理所有其他不正确的参数一样。

第四,其他所有不正确的参数数据都会被IAE,为什么不一致呢?为什么非法null是如此特殊,以至于它应该从所有其他类型的非法参数中获得一个单独的异常?

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

其他回答

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

我倾向于遵循JDK库的设计,特别是集合和并发(Joshua Bloch, Doug Lea,这些人知道如何设计可靠的api)。不管怎样,JDK中的许多api都会主动抛出NullPointerException。

例如,Javadoc For Map。containsKey状态:

如果键为空,则@抛出NullPointerException 不允许空键(可选)。

举办自己的NPE是完全合理的。约定是在异常消息中包含为空的参数名。

模式是这样的:

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

无论您做什么,都不要允许设置一个错误的值,并在其他代码尝试使用它时抛出异常。这使得调试成为一场噩梦。你应该始终遵循“快速失败”的原则。

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

如果你在使用它,它是空的,NullPointer。如果它被传入并且它为空,则为非法参数。

给杰森·科恩的论点投了一票,因为它表现得很好。让我一步一步地分解它。: -)

The NPE JavaDoc explicitly says, "other illegal uses of the null object". If it was just limited to situations where the runtime encounters a null when it shouldn't, all such cases could be defined far more succinctly. Can't help it if you assume the wrong thing, but assuming encapsulation is applied properly, you really shouldn't care or notice whether a null was dereferenced inappropriately vs. whether a method detected an inappropriate null and fired an exception off. I'd choose NPE over IAE for multiple reasons It is more specific about the nature of the illegal operation Logic that mistakenly allows nulls tends to be very different from logic that mistakenly allows illegal values. For example, if I'm validating data entered by a user, if I get value that is unacceptable, the source of that error is with the end user of the application. If I get a null, that's programmer error. Invalid values can cause things like stack overflows, out of memory errors, parsing exceptions, etc. Indeed, most errors generally present, at some point, as an invalid value in some method call. For this reason I see IAE as actually the MOST GENERAL of all exceptions under RuntimeException. Actually, other invalid arguments can result in all kinds of other exceptions. UnknownHostException, FileNotFoundException, a variety of syntax error exceptions, IndexOutOfBoundsException, authentication failures, etc., etc.

总的来说,我觉得NPE受到了很大的诋毁,因为传统上一直与未能遵循快速失效原则的代码联系在一起。再加上JDK未能用消息字符串填充NPE,这确实产生了一种强烈的负面情绪,这种情绪是没有根据的。实际上,从运行时的角度来看,NPE和IAE之间的区别仅限于名称。从这个角度来看,你的名字越精确,你给调用者的信息就越清晰。

抛出一个排除空参数的异常(无论是NullPointerException还是自定义类型)使得自动化空测试更加可靠。这种自动化测试可以通过反射和一组默认值来完成,就像在Guava的NullPointerTester中一样。例如,NullPointerTester将尝试调用以下方法…

Foo(String string, List<?> list) {
  checkArgument(string.length() > 0);
  // missing null check for list!
  this.string = string;
  this.list = list;
}

...with two lists of arguments: "", null and null, ImmutableList.of(). It would test that each of these calls throws the expected NullPointerException. For this implementation, passing a null list does not produce NullPointerException. It does, however, happen to produce an IllegalArgumentException because NullPointerTester happens to use a default string of "". If NullPointerTester expects only NullPointerException for null values, it catches the bug. If it expects IllegalArgumentException, it misses it.