IllegalArgumentException 或 NullPointerException 用于 null 参数?[关闭]

IllegalArgumentException or NullPointerException for a null parameter? [closed]

提问人:Mike Stone 提问时间:8/7/2008 最后编辑:starsplusplusMike Stone 更新时间:10/4/2022 访问量:201357

问:


想改进这个问题吗?更新问题,以便可以通过编辑这篇文章用事实和引文来回答。

7年前关闭。

我有一个简单的属性 setter 方法,不适合这个特定的属性。在这种情况下,我总是感到困惑:我应该抛出 IllegalArgumentException 还是 NullPointerException?从 javadocs 来看,两者似乎都很合适。有没有某种可理解的标准?或者这只是你应该做任何你喜欢的事情之一,而且两者都是正确的?null

java 异常 null nullpointerexception illegalargumentexception

评论


答:

313赞 Greg Hurlman 8/7/2008 #1

如果您不想成为允许的值,似乎需要 an,如果您尝试使用结果为 .IllegalArgumentExceptionnullNullPointerExceptionnull

评论

35赞 SamStephens 2/7/2014
以下对此问题的回答提出了令人信服的论点,即 NullPointerException 是正确的异常: stackoverflow.com/a/8160/372926 ;stackoverflow.com/a/8196334/372926 ;和 stackoverflow.com/a/6358/372926
4赞 matoni 7/22/2018
IllegalArgumentException与 Java 的 Objects.requireNonNull(T) 和 Guava 的 Preconditions.checkNotNull(T) 相矛盾,后者会抛出一个 .然而,正如杰森·科恩(Jason Cohen)的出色回答及其评论部分所解释的那样,正确的答案是肯定的。NullPointerExceptionIllegalArgumentException
18赞 Jeremy Privett 8/7/2008 #2

如果它是一个方法并且正在传递给它,我认为抛出一个 .在您尝试实际使用 .setternullIllegalArgumentExceptionNullPointerExceptionnull

所以,如果你正在使用它,它是 , .如果它被传入并且是 , .nullNullPointernullIllegalArgument

75赞 Mark Renouf 8/9/2008 #3

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

例如,Javadoc 表示:Map.containsKey

@throws NullPointerException,如果键为 null,并且此映射 不允许 null 键(可选)。

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

模式是这样的:

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

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

评论

3赞 Gili 12/30/2010
值得深思:也许 NullPointerException 不扩展 IllegalArgumentException 的原因是前者可能发生在不涉及方法参数的情况下。
2赞 Lie Ryan 3/1/2013
@Gili:也许这个问题首先存在,因为 Java 不支持多重继承。如果 Java 支持 MI,则可以抛出继承自 IllegalArgumentException 和 NullPointerException 的异常。
7赞 PhiLho 7/22/2013
该消息不需要包含参数,因为它始终为 null,给出:“null must not be null”,不是很有用。:-)否则,我同意,您可以制作一个“丰富”的 NPE,其中包含有意义的信息。
5赞 flamming_python 7/1/2014
我必须同意 - 如有疑问,请遵循标准 API。请注意,并非 API 中的所有内容都是最佳的,但它仍然由数百名开发人员维护和迭代,并被数百万程序员使用。现在在 Java 7 中,我们有另一个以这种方式使用 NPE 的例子;Objects.requireNonNull(T obj) 方法 - 明确指定用于检查对象引用是否为非 null,明确指定用于在方法/构造函数中执行参数验证,并明确指定在对象为 null 时抛出 NPE。结束
-5赞 jassuncao 8/11/2008 #4

如果选择引发 NPE,并且在方法中使用参数,则显式检查 null 可能是冗余且成本高昂的。我认为VM已经为您做到了这一点。

评论

0赞 mP. 3/31/2012
运行时不会包含有意义的消息。
0赞 Jin Kwon 7/13/2012
实际上,这个评论可以得出一些其他意见。让 VM 说话,但程序员如果愿意,可以在 VM 之前说话。NPEIAE
1赞 PhiLho 7/22/2013
贵?我不认为 == null 那么贵......此外,null 参数可以只存储以供以后使用,并且会在方法调用后很长时间抛出异常,从而使错误更难跟踪。或者,可以在使用 null 参数之前创建一个开销很大的对象,依此类推。早期检测似乎是一个不错的选择。
1赞 digital_infinity 8/11/2017
对这个答案投反对票是对背后硬件的误解。当然,硬件检查(CPU 所做的)比显式检查便宜。取消引用 null 是 SegmentationFault (SegV) 特殊情况(访问不属于进程的页面),CPU/OS 检查和 JRE 将其作为抛出 NullPointerException 的特殊情况进行处理。
6赞 Claude Houle 8/12/2008 #5

公认的做法是使用 IllegalArgumentException( String message ) 声明参数无效并尽可能多地提供详细信息......因此,要说发现参数为 null,而异常为 non-null,您可以执行如下操作:

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

您几乎没有理由隐式使用“NullPointerException”。NullPointerException 是 Java 虚拟机在尝试对 null 引用执行代码时抛出的异常(如 toString())。

171赞 GaryF 8/12/2008 #6

标准是抛出.通常无误的“有效 Java”在第 42 项(第一版)、第 60 项(第二版)或第 72 项(第三版)“赞成使用标准例外”中简要讨论了这一点:NullPointerException

“可以说,所有错误的方法 调用归结为非法 争论或非法状态,但其他 例外通常用于 某些非法的论点和 国家。如果调用方传入 null 某些参数,其值为 null 是被禁止的,公约规定 引发 NullPointerException 而不是 IllegalArgumentException。

评论

102赞 Thorbjørn Ravn Andersen 5/30/2009
我强烈不同意。仅当 JVM 意外遵循 null 引用时,才应抛出 NullPointerExceptions。这就是在凌晨 3 点被叫来查看代码时对你有帮助的区别。
27赞 GaryF 6/8/2009
我不一定同意这个标准(我实际上可以在这个问题上采取任何一种方式),但这就是整个 JDK 的标准用法,因此 Effective Java 为它提供了理由。我认为这是一个选择是否遵循标准,或者做你认为正确的事情的情况。除非你有很好的理由(这当然可能符合条件),否则最好遵循标准做法。
22赞 Jim Balter 2/1/2013
异常跟踪显示异常点,因此,如果异常类型的差异导致您陷入困境,或者是“帮助您的差异”,那么您做错了什么。
9赞 Jim Balter 7/8/2014
幻想和诡辩。在下面,MB 写道:“请注意,关于硬调试的论点是虚假的,因为您当然可以向 NullPointerException 提供一条消息,说明什么是 null 以及为什么它不应该为 null。就像 IllegalArgumentException 一样。
13赞 GaryF 7/8/2014
作为这里的原始回答者,让我说这并不重要。这当然不值得 6 年的对话。随心所欲地选择一个,并保持一致。正如我所指出的,标准是NPE。如果您出于任何原因更喜欢 IAE,那就去吧。只要保持一致。
-1赞 martinatime 8/17/2008 #7

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

这里最大的区别是,在检查方法的参数是否有效时,应该使用 IllegalArgumentException。每当对象为 null 时被“使用”时,都应该使用 NullPointerException。

我希望这有助于正确看待两者。

评论

1赞 Christopher Smith 12/9/2009
突出的是,使用 null 的是应用程序,而不是运行时。因此,“当一个方法被传递了一个非法或不适当的参数时”和“当应用程序使用 null 时”之间存在相当大的重叠。从理论上讲,如果应用为需要非 null 的字段传递 null,则这两个条件都得到满足。
7赞 Allain Lalonde 8/22/2008 #8

完全同意所说的话。早失败,快速失败。相当不错的例外咒语。

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

我的 2 美分

-1赞 erickson 8/29/2008 #9

如果它是一个“setter”,或者我打算让一个成员稍后使用,我倾向于使用 IllegalArgumentException。

如果这是我现在要在方法中使用的(取消引用),我会主动抛出 NullPointerException。我更喜欢这样做,而不是让运行时这样做,因为我可以提供有用的消息(似乎运行时也可以这样做,但这是另一天的咆哮)。

如果我要重写某个方法,我会使用被重写的方法使用的任何方法。

5赞 Martin OConnor 9/5/2008 #10

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

评论

9赞 Donz 11/9/2010
NPE 上的 JavaDoc 有另一种观点:“应用程序应该抛出此类的实例,以指示对 null 对象的其他非法使用。不要那么绝对
444赞 Jason Cohen 9/7/2008 #11

出于以下原因,您应该使用 (IAE),而不是 (NPE):IllegalArgumentExceptionNullPointerException

首先,NPE JavaDoc 明确列出了适合 NPE 的情况。请注意,当使用不当时,它们都是由运行时抛出的。相比之下,IAE JavaDoc 再清楚不过了:“抛出以指示方法已传递了非法或不适当的参数。是的,就是你!null

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

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

第四,所有其他不正确的参数数据都会被IAE,那么为什么不保持一致呢?为什么非法的论点如此特殊,以至于它应该与所有其他类型的非法论点分开?null

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

评论

135赞 Gili 12/30/2010
Effective Java 2nd Edition, Item 60:“可以说,所有错误的方法调用都归结为非法参数或非法状态,但其他例外通常用于某些类型的非法参数和状态。如果调用方在禁止 null 值的某个参数中传递 null,则约定将引发 NullPointerException 而不是 IllegalArgumentException。同样,如果调用方将表示索引的参数中的超出范围的值传递到序列中,则应引发 IndexOutOfBoundsException 而不是 IllegalArgumentException。
35赞 R. Martinho Fernandes 1/26/2011
NPE 的 JavaDoc 还声明了以下内容:“应用程序应该抛出此类的实例,以指示对 null 对象的其他非法使用。这个可以更清楚:(
13赞 Aaron Digulla 10/15/2012
不幸的是,验证方法 (commons lang) 和 (guava) 都抛出了 NPE :-(Validate.notNullPreconditions.checkNotNull
2赞 michaelok 12/17/2013
虽然 Guava 也有 Preconditions.checkArgument() 抛出 IllegalArgumentException ...
17赞 Arto Bendiken 10/29/2014
@AaronDigulla,来自 Guava 文档:“我们意识到有许多有效的论据支持将 IAE 抛给空参数。事实上,如果我们有一台时光机回到 15 年前>,我们甚至可能会尝试将事情推向那个方向。但是,我们决定坚持使用 NullPointerException 的 JDK 和 Effective Java 首选项。如果你坚定地相信 IAE 是正确的,你仍然拥有 ,只是没有它返回 arg 的便利,或者你可以为你的项目创建一个本地实用程序。code.google.com/p/guava-libraries/wiki/IdeaGraveyardcheckArgument(arg != null)
-1赞 Will Sargent 9/9/2008 #12

你应该抛出一个 IllegalArgumentException,因为它会让程序员明显地知道他做了一些无效的事情。开发人员已经习惯于看到 VM 抛出的 NPE,以至于任何程序员都不会立即意识到他的错误,并且会开始随机环顾四周,或者更糟的是,责怪你的代码“有问题”。

评论

4赞 Christopher Smith 12/9/2009
对不起,如果程序员在遇到任何异常时“随机”环顾四周......更改异常的名称不会有太大帮助。
22赞 Steve McLeod 10/17/2008 #13

这是一个“圣战”式的问题。换句话说,这两种选择都是好的,但人们会有自己的偏好,他们会捍卫到死。

评论

0赞 Mr.Q 2/24/2019
不,这个问题只有一个正确答案,那就是当方法的输入不正确时,使用 throw IllegalArgument 异常。此外,在开发环境中,您可以使用断言来检查输入的有效性并引发适当的异常;)
0赞 Solomon Ucko 7/23/2019
@Mr.Q 我认为应该抛出这一点:这是 JDK 使用的约定,并且需要接口,它更具体(就像 ,等等)等等。NullPointerExceptionIndexOutOfBoundsException
0赞 Yousha Aleayoub 11/25/2019
kekekekkek... :D
-1赞 10/30/2008 #14

在这种情况下,IllegalArgumentException 会向使用 API 的用户传达明确的信息,即“不应为 null”。正如其他论坛用户所指出的,只要您使用 API 向用户传达正确的信息,就可以使用 NPE。

GaryF 和 tweakt 删除了推荐使用 NPE 的“有效 Java”(我发誓)参考。看看其他好的 API 是如何构建的,是了解如何构建你的 API 的最佳方式。

另一个很好的例子是查看 Spring API。例如,org.springframework.beans.BeanUtils.instantiateClass(Constructor ctor, Object[] args) 有一个 Assert.notNull(ctor, “Constructor must not be null”) 行。org.springframework.util.Assert.notNull(Object object, String message) 方法检查传入的参数(对象)是否为 null,如果是,它会抛出一个新的 IllegalArgumentException(message),然后在 org.springframework.beans.BeanUtils.instantiateClass(...) 方法中捕获该参数。

4赞 Jason Fritcher 1/17/2009 #15

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

评论

0赞 Thorbjørn Ravn Andersen 5/30/2009
我采用了“throw new IllegalArgumentException(”foo == null“)”方法。无论如何,您都需要记录变量名称(以确保您正在查看正确的位置等)
10赞 Brian T. Grant 7/2/2009 #16

Apache Commons Lang 有一个 NullArgumentException,它执行了此处讨论的许多操作:它扩展了 IllegalArgumentException,并且其唯一的构造函数采用参数的名称,该参数应该是非 null。

虽然我觉得抛出像 NullArgumentException 或 IllegalArgumentException 这样的东西可以更准确地描述特殊情况,但我和我的同事们选择听从布洛赫关于这个问题的建议。

评论

8赞 artbristol 9/18/2013
请注意,他们从 commons-lang3 中删除了它:apache-commons.680414.n4.nabble.com/...
49赞 Christopher Smith 12/9/2009 #17

投票支持杰森·科恩(Jason Cohen)的论点,因为它得到了很好的阐述。让我一步一步地肢解它。;-)

  • NPE JavaDoc 明确指出,“对 null 对象的其他非法使用”。如果它只是局限于运行时在不应该遇到 null 的情况,那么可以更简洁地定义所有这些情况。

  • 如果你假设了错误的事情,那就没办法了,但假设封装应用得当,你真的不应该关心或注意到是否不恰当地取消了 null 的引用,以及方法是否检测到不适当的 null 并触发了异常。

  • 出于多种原因,我会选择 NPE 而不是 IAE

    • 它更具体地说明了非法操作的性质
    • 错误地允许 null 值的逻辑往往与错误地允许非法值的逻辑有很大不同。例如,如果我正在验证用户输入的数据,如果我得到的值是不可接受的,则该错误的根源在于应用程序的最终用户。如果我得到一个空,那就是程序员错误。
    • 无效值可能会导致堆栈溢出、内存不足错误、解析异常等情况。事实上,大多数错误通常在某些时候表现为某些方法调用中的无效值。出于这个原因,我认为 IAE 实际上是 RuntimeException 下所有异常中最通用的。
  • 实际上,其他无效参数可能会导致各种其他异常。UnknownHostExceptionFileNotFoundException、各种语法错误异常、IndexOutOfBoundsException、认证失败等。

总的来说,我觉得 NPE 受到了很多诽谤,因为传统上它与不遵循快速故障原则的代码相关联。再加上 JDK 未能用消息字符串填充 NPE,这确实造成了一种强烈的负面情绪,这种情绪没有充分的根据。事实上,从运行时的角度来看,NPE 和 IAE 之间的区别严格在于名称。从这个角度来看,你对名字越精确,你给来电者就越清晰。

评论

0赞 Thorbjørn Ravn Andersen 7/7/2014
大多数未经检查的异常之间的区别只是名称。
152赞 MB. 11/20/2011 #18

我完全赞成抛出空参数,直到今天,当我注意到 Java 7 中的方法时。使用这种方法,而不是执行以下操作:IllegalArgumentExceptionjava.util.Objects.requireNonNull

if (param == null) {
    throw new IllegalArgumentException("param cannot be null.");
}

您可以执行以下操作:

Objects.requireNonNull(param);

如果您传递给它的参数是 ,它将抛出一个。NullPointerExceptionnull

鉴于这种方法在中间是正确的,我认为它的存在是一个非常有力的迹象,表明投掷是“Java 的做事方式”。java.utilNullPointerException

我想无论如何我都决定了。

请注意,关于硬调试的论点是虚假的,因为您当然可以提供一条消息来说明什么是空的,以及为什么它不应该是空的。就像 .NullPointerExceptionIllegalArgumentException

另一个优点是,在高性能关键代码中,您可以省去对 null 的显式检查(以及带有友好错误消息的 a),而只需依赖在对 null 参数调用方法时自动获得的 u。如果你快速调用一个方法(即快速失败),那么你基本上会得到相同的效果,只是对开发人员来说不太友好。大多数情况下,最好显式检查并抛出一条有用的消息来指示哪个参数为 null,但如果性能要求,可以选择在不破坏方法/构造函数的已发布约定的情况下更改它。NullPointerExceptionNullPointerExceptionNullPointerException

评论

17赞 assylias 10/15/2012
番石榴也抛出NPE。Preconditions.checkNotNull(arg)
16赞 Bogdan Calmac 3/9/2013
这并没有真正为非法 null ARGUMENTS 的 NullPointerException 增加更多权重。JDK requireNonNull() 和 Guava checkNotNull() 都可以在代码中的任何位置使用任何对象调用。例如,您可以在循环中调用它们。requireNotNull() 和 checkNotNull() 不可能假设使用某些方法参数调用并抛出 IllegalArgumentException。请注意,Guava 也有 Preconditions.checkArgument(),它确实会抛出 IllegalArgumentException。
2赞 MB. 8/8/2013
Bogdan 的一个公平观点,尽管我怀疑 requireNonNull 的典型(和通常预期)使用是用于参数检查。如果你需要检查循环中的某些东西是否为空,我会认为断言将是典型的方式。
21赞 Richard Zschech 2/2/2015
我认为 Object.requireNonNull() 的 JavaDoc 增加了参数的权重:“此方法主要用于在方法和构造函数中进行参数验证”
0赞 BeeOnRope 11/3/2016
请记住,支持隐式 null 检查的性能参数通常是无效的。JIT 能够识别用户 null 检查并省略以下隐含的 null 检查,和/或对任一类型的 null 检查使用相同的优化集。有关详细信息,请参阅此 wiki,特别是: 在大多数情况下,用户编写的 null 检查在功能上与 JVM 插入的检查相同。当然,如果你在 null 中做一些更高级的事情,比如自定义消息,那么这种优化可能不适用。
4赞 Luis Daniel Mesa Velasquez 4/18/2012 #19

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

throw new IllegalArgumentException(new NullPointerException(NULL_ARGUMENT_IN_METHOD_BAD_BOY_BAD));

评论

1赞 maaartinus 1/30/2014
这将使异常创建的开销增加一倍,并且不会真正帮助,因为捕获不会执行任何操作。唯一能帮上忙的是,但我们没有多重继承权。NullPointerExceptionIllegalNullPointerArgumentException extends IllegalArgumentException, NullPointerException
0赞 Sgene9 1/27/2018
我认为,更具体的例外应该用更笼统的例外来包装。NPE 用于表达式,而 IAE 用于方法。由于方法包含包含表达式的语句,因此 IAE 更通用。
0赞 Sgene9 1/27/2018
关于开销,我不知道。但是,由于堆栈跟踪基本上是相同的,只是异常的名称在中间发生了变化,因此我认为双重异常的开销应该不会太大。如果担心开销,他可以使用“if”语句返回 null 或 -1,而不是抛出异常。
5赞 Chris Povirk 11/10/2012 #20

某些集合假定使用 而不是 拒绝 .例如,如果将包含的集合与拒绝的集合进行比较,则第一个集合将调用另一个集合并捕获其 -- 但不会。(我正在研究 的实现。nullNullPointerExceptionIllegalArgumentExceptionnullnullcontainsAllNullPointerExceptionIllegalArgumentExceptionAbstractSet.equals

您可以合理地争辩说,以这种方式使用未经检查的异常是一种反模式,将包含的集合与不能包含的集合进行比较可能是一个错误,实际上应该产生异常,或者根本不应该放入集合是一个坏主意。然而,除非你愿意说在这种情况下应该抛出一个例外,否则你只能记住在某些情况下是必需的,但在其他情况下则不是。(“NPE 之前的 IAE,'c'之后除外......”)nullnullnullequalsNullPointerException

类似地,生成工具可以自动插入 null 检查。值得注意的是,Kotlin 的编译器在将可能为 null 的值传递给 Java API 时会这样做。当检查失败时,结果是 .因此,要为您拥有的任何 Kotlin 用户和 Java 用户提供一致的行为,您需要使用 .NullPointerExceptionNullPointerException

评论

0赞 Alowaniak 7/8/2016
我不明白包含如何根据集合中的元素抛出 NPE。抛出 NPE (afaict) 的唯一原因是集合本身为 null(在这种情况下,抛出 NPE 是因为它试图访问其迭代器)。然而,这确实提出了一个问题,即是否应该检查空输入,或者它是否应该传播,直到它尝试访问。
2赞 Chris Povirk 7/8/2016
new TreeSet<>().containsAll(Arrays.asList((Object) null));抛出,因为包含 .NPEListnull
1赞 Alowaniak 7/12/2016
啊,确实,TreeSet#contains 会抛出 NPE,“如果指定的元素为 null 并且该集合使用自然排序,或者其比较器不允许 null 元素”。只看了允许null的AbstractSet,我的错。就我个人而言,我觉得很奇怪,它不仅返回 false,因为在这种情况下不可能添加 null。
6赞 Chris Povirk 11/10/2012 #21

抛出参数(无论是自定义类型还是自定义类型)独有的异常,使自动化测试更加可靠。这种自动化测试可以通过反射和一组默认值来完成,就像 GuavaNullPointerTester 一样。例如,将尝试调用以下方法...nullNullPointerExceptionnullNullPointerTester

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

...具有两个参数列表:和 .它将测试这些调用中的每一个都抛出预期的 .对于此实现,传递列表不会生成 .但是,它确实碰巧产生一个,因为碰巧使用默认字符串 。如果只期望值,它会捕获错误。如果它期望,它就会错过它。"", nullnull, ImmutableList.of()NullPointerExceptionnullNullPointerExceptionIllegalArgumentExceptionNullPointerTester""NullPointerTesterNullPointerExceptionnullIllegalArgumentException

8赞 Sascha Baumeister 1/1/2013 #22

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

  • 必须尽快指示参数约束冲突(->快速失败),以避免更难调试的非法状态
  • 如果由于某种原因导致无效的 null 指针,请抛出 NullPointerException
  • 如果数组/集合索引非法,则抛出 ArrayIndexOutOfBounds
  • 如果数组/集合大小为负,则抛出 NegativeArraySizeException
  • 如果存在上述未涵盖的非法参数,并且您没有其他更具体的异常类型,请将 IllegalArgumentException 作为废纸篓抛出
  • 另一方面,如果由于某种正当原因无法通过快速失败来避免的约束违反,请捕获并重新抛出为 IllegalStateException 或更具体的已检查异常。在这种情况下,永远不要让传递原始的 NullPointerException、ArrayIndexOutOfBounds 等!

至少有三个很好的理由反对将各种参数约束冲突映射到 IllegalArgumentException,第三个理由可能非常严重,以至于将实践标记为不良风格:

(1) 程序员不能安全地假设所有违反参数约束的情况都会导致 IllegalArgumentException,因为如果没有更具体的异常类型可用,绝大多数标准类都会使用此异常,而不是作为废纸篓。尝试将所有违反参数约束的情况映射到 API 中的 IllegalArgumentException 只会导致程序员在使用类时感到沮丧,因为标准库大多遵循违反您的规则的不同规则,并且大多数 API 用户也会使用它们!

(2) 映射异常实际上会导致另一种异常,由单继承引起:所有 Java 异常都是类,因此仅支持单继承。因此,没有办法创建一个真正同时称为 NullPointerException 和 IllegalArgumentException 的异常,因为子类只能从一个或另一个继承。因此,在 null 参数的情况下抛出 IllegalArgumentException 会使 API 用户在程序尝试以编程方式纠正问题时更难区分问题,例如通过将默认值输入到调用重复中!

(3) 映射实际上会产生错误掩蔽的危险:为了将参数约束冲突映射到 IllegalArgumentException,您需要在具有任何约束参数的每个方法中编写外部 try-catch。但是,简单地在此 catch 块中捕获 RuntimeException 是不可能的,因为这可能会将您中使用的自由方法抛出的记录的 RuntimeExceptions 映射到 IllegalArgumentException 中,即使它们不是由参数约束冲突引起的。因此,您需要非常具体,但即使这样做也无法保护您免受意外将另一个 API 的未记录运行时异常(即 bug)映射到 API 的 IllegalArgumentException 的情况。因此,即使是最仔细的映射也有可能将其他库制作者的编程错误掩盖为违反方法用户的参数约束,这简直是滑稽的行为!

另一方面,在标准做法中,规则保持简单,异常原因保持不被掩盖和具体。对于方法调用者来说,规则也很简单: - 如果由于传递了非法值而遇到任何类型的记录的运行时异常,请使用默认值重复调用(为此,必须有特定的异常),或者更正代码 - 另一方面,如果您遇到未记录在给定参数集的运行时异常,请向方法的制作者提交错误报告,以确保他们的代码或文档已修复。

4赞 leo 6/5/2015 #23

根据您的情况,是最佳选择,因为这不是您的属性的有效值。IllegalArgumentExceptionnull

4赞 Nitesh Soomani 6/23/2015 #24

NullPointerException尝试访问具有当前值为 的引用变量的对象时抛出。null

IllegalArgumentException当方法接收到的参数格式与方法预期不同时引发。

-1赞 vijay 8/18/2016 #25

理想情况下,不应引发运行时异常。应为方案创建已选中的异常(业务异常)。因为如果抛出并记录了这些异常中的任何一个,则在浏览日志时会误导开发人员。相反,业务异常不会造成这种恐慌,并且在对日志进行故障排除时通常会忽略。

8赞 Ben Seidel 9/23/2016 #26

作为一个主观问题,这应该被关闭,但它仍然是开放的:

这是我以前工作地点使用的内部政策的一部分,效果非常好。这都是凭记忆,所以我不记得确切的措辞。值得注意的是,他们没有使用选中的异常,但这超出了问题的范围。他们使用的未经检查的异常分为 3 个主要类别。

NullPointerException:不要故意抛出。NPE 仅在取消引用 null 引用时由 VM 引发。将尽一切可能的努力确保这些永远不会被扔掉。@Nullable 和 @NotNull 应与代码分析工具结合使用,以查找这些错误。

IllegalArgumentException:当函数的参数不符合公共文档时引发,以便可以根据传入的参数来识别和描述错误。OP的情况就属于这一类。

IllegalStateException:当调用函数并且其参数在传递时意外或与方法所属对象的状态不兼容时引发。

例如,有两个内部版本的 IndexOutOfBoundsException 用于具有长度的事物。一个是 IllegalStateException 的子类,如果索引大于长度,则使用。另一个是 IllegalArgumentException 的子类,如果索引为负数,则使用。这是因为您可以向对象添加更多项目,并且参数将有效,而负数永远不会有效。

正如我所说,这个系统运行得非常好,需要有人来解释为什么会有这种区别:“根据错误的类型,你很容易弄清楚该怎么做。即使您实际上无法弄清楚出了什么问题,您也可以找出在哪里捕获该错误并创建其他调试信息。

NullPointerException:处理 Null 情况或放入断言,以便不引发 NPE。如果你输入断言只是其他两种类型之一。如果可能,请继续调试,就好像断言一开始就存在一样。

IllegalArgumentException:您的呼叫站点有问题。如果传入的值来自另一个函数,请找出收到错误值的原因。如果要传入其中一个参数,则传播错误,将检查调用堆栈,直到找到未返回预期内容的函数。

IllegalStateException:您没有按正确的顺序调用函数。如果您正在使用其中一个参数,请检查它们并抛出描述问题的 IllegalArgumentException。然后,您可以将脸颊向上传播到堆栈上,直到找到问题。

无论如何,他的观点是,您只能将 IllegalArgumentAssertions 复制到堆栈中。您无法将 IllegalStateExceptions 或 NullPointerExceptions 传播到堆栈中,因为它们与您的函数有关。