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


当前回答

一些集合假设使用NullPointerException而不是IllegalArgumentException拒绝null。例如,如果你比较一个包含null的集合和一个拒绝null的集合,第一个集合将调用另一个集合的containsAll,并捕获它的NullPointerException——而不是IllegalArgumentException。(我正在查看AbstractSet.equals的实现。)

You could reasonably argue that using unchecked exceptions in this way is an antipattern, that comparing collections that contain null to collections that can't contain null is a likely bug that really should produce an exception, or that putting null in a collection at all is a bad idea. Nevertheless, unless you're willing to say that equals should throw an exception in such a case, you're stuck remembering that NullPointerException is required in certain circumstances but not in others. ("IAE before NPE except after 'c'...")

类似地,构建工具可以自动插入空检查。值得注意的是,Kotlin的编译器在向Java API传递可能为空的值时执行此操作。当检查失败时,结果是NullPointerException。因此,为了给所有的Kotlin用户和Java用户提供一致的行为,您需要使用NullPointerException。

其他回答

实际上,在我看来,抛出IllegalArgumentException或NullPointerException的问题只是对Java中不完全理解异常处理的少数人的“圣战”。一般来说,规则很简单,如下:

argument constraint violations must be indicated as fast as possible (-> fast fail), in order to avoid illegal states which are much harder to debug in case of an invalid null pointer for whatever reason, throw NullPointerException in case of an illegal array/collection index, throw ArrayIndexOutOfBounds in case of a negative array/collection size, throw NegativeArraySizeException in case of an illegal argument that is not covered by the above, and for which you don't have another more specific exception type, throw IllegalArgumentException as a wastebasket on the other hand, in case of a constraint violation WITHIN A FIELD that could not be avoided by fast fail for some valid reason, catch and rethrow as IllegalStateException or a more specific checked exception. Never let pass the original NullPointerException, ArrayIndexOutOfBounds, etc in this case!

至少有三个非常好的理由反对将所有类型的参数约束违反映射到IllegalArgumentException,第三个理由可能非常严重,以至于标志着这种做法的糟糕风格:

(1) A programmer cannot a safely assume that all cases of argument constraint violations result in IllegalArgumentException, because the large majority of standard classes use this exception rather as a wastebasket if there is no more specific kind of exception available. Trying to map all cases of argument constraint violations to IllegalArgumentException in your API only leads to programmer frustration using your classes, as the standard libraries mostly follow different rules that violate yours, and most of your API users will use them as well!

(2) Mapping the exceptions actually results in a different kind of anomaly, caused by single inheritance: All Java exceptions are classes, and therefore support single inheritance only. Therefore, there is no way to create an exception that is truly say both a NullPointerException and an IllegalArgumentException, as subclasses can only inherit from one or the other. Throwing an IllegalArgumentException in case of a null argument therefore makes it harder for API users to distinguish between problems whenever a program tries to programmatically correct the problem, for example by feeding default values into a call repeat!

(3) Mapping actually creates the danger of bug masking: In order to map argument constraint violations into IllegalArgumentException, you'll need to code an outer try-catch within every method that has any constrained arguments. However, simply catching RuntimeException in this catch block is out of the question, because that risks mapping documented RuntimeExceptions thrown by libery methods used within yours into IllegalArgumentException, even if they are no caused by argument constraint violations. So you need to be very specific, but even that effort doesn't protect you from the case that you accidentally map an undocumented runtime exception of another API (i.e. a bug) into an IllegalArgumentException of your API. Even the most careful mapping therefore risks masking programming errors of other library makers as argument constraint violations of your method's users, which is simply hillareous behavior!

With the standard practice on the other hand, the rules stay simple, and exception causes stay unmasked and specific. For the method caller, the rules are easy as well: - if you encounter a documented runtime exception of any kind because you passed an illegal value, either repeat the call with a default (for this specific exceptions are neccessary), or correct your code - if on the other hand you enccounter a runtime exception that is not documented to happen for a given set of arguments, file a bug report to the method's makers to ensure that either their code or their documentation is fixed.

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

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

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

二分法……它们不重叠吗?只有整体中不重叠的部分才能构成二分法。在我看来:

throw new IllegalArgumentException(new NullPointerException(NULL_ARGUMENT_IN_METHOD_BAD_BOY_BAD));

在这种情况下,IllegalArgumentException使用API向用户传达了“不应该为空”的明确信息。正如其他论坛用户指出的那样,只要你使用API向用户传达正确的信息,你就可以使用NPE。

GaryF和tweak放弃了“有效Java”(我发誓)的参考,建议使用NPE。看看其他好的API是如何构造的,这是了解如何构造你的API的最好方法。

Another good example is to look at the Spring APIs. For example, org.springframework.beans.BeanUtils.instantiateClass(Constructor ctor, Object[] args) has a Assert.notNull(ctor, "Constructor must not be null") line. org.springframework.util.Assert.notNull(Object object, String message) method checks to see if the argument (object) passed in is null and if it is it throws a new IllegalArgumentException(message) which is then caught in the org.springframework.beans.BeanUtils.instantiateClass(...) method.