对于null参数,IllegalArgumentException或NullPointerException?

我有一个简单的setter方法为一个属性和null不适合这个特定的属性。 我一直在这种情况下被撕毁:我应该抛出一个IllegalArgumentException ,或一个NullPointerException ? 从javadocs,这两个看起来都合适。 有一种被理解的标准吗? 或者这只是你应该做的任何事情之一,而且都是正确的?

如果你不想让null成为一个允许的值,那么看起来像是调用了IllegalArgumentException ,如果你试图使用一个variables为null话,就会抛出NullPointerException

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

首先, NPE JavaDoc明确列出了NPE适用的情况。 注意,当null被不恰当地使用 ,所有这些都是由运行时抛出 。 相比之下, IAE的JavaDoc不能更加清晰:“抛出以表明某种方法已被通过非法或不恰当的论点”。 是的,就是你!

其次,当你看到堆栈跟踪中的NPE时,你认为是什么? 大概有人解除了null 。 当你看到IAE时,你认为堆栈顶部的方法的调用者传入了一个非法的值。 再一次,后一种假设是真实的,前者是误导性的。

第三,由于IAE是为validation参数而devise的,所以你必须把它作为例外的默认select,那么为什么要selectNPE呢? 当然,不是因为不同的行为 – 你真的希望调用代码从IAE中分别捕获NPE,并做出不同的结果吗? 你想传达一个更具体的错误信息? 但是,无论如何,您可以在exception消息文本中这样做,因为您应该为所有其他不正确的参数。

第四,所有其他不正确的参数数据将是IAE,那么为什么不一致呢? 为什么非法的null是非常特殊的,它应该与其他所有types的非法理由分开处理呢?

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

标准是抛出NullPointerException。 一般可靠的“Effective Java”在第42条(第一版)或第60条(第二版)中简要讨论了“赞成使用标准例外”:

“可以说,所有错误的方法调用归结为一个非法的参数或非法的状态,但是其他的例外是标准的用于某些types的非法参数和状态。如果调用者在某些禁止null值的参数中传递null,约定规定抛出NullPointerException而不是IllegalArgumentException。

我一致赞成为null参数抛出IllegalArgumentException ,直到今天,当我注意到Java 7中的java.util.Objects.requireNonNull方法。使用该方法,而不是:

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

你可以做:

 Objects.requireNonNull(param); 

如果你传递的参数为null ,它会抛出一个NullPointerException

鉴于这种方法在java.util是正确的,我认为抛出NullPointerException是“Java的做事方式”的一个非常明显的表示。

无论如何,我想我是决定的。

注意关于硬性debugging的参数是假的,因为你当然可以给NullPointerException提供一个消息,说明什么是null,为什么它不应该是null。 就像IllegalArgumentException

NullPointerException一个附加优点是,在高性能的关键代码中,可以免除显式检查null(以及带有友好错误消息的NullPointerException ),并且只依赖于在调用方法时自动获得的NullPointerException在null参数上。 假如你快速地调用一个方法(即快速失败),那么你的效果基本相同,只是对开发人员不太友好。 大多数情况下,最好是明确地检查并抛出一个有用的消息来表明哪个参数是空的,但如果性能决定不违反方法/构造函数的已发布协议,可以select更改该选项。

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

例如, Map.containsKey的Javadoc指出:

如果键为空,并且该映射不允许空键(可选),则抛出NullPointerException。

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

模式是:

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

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

投给杰森·科恩(Jason Cohen)的观点是因为这个观点很好。 让我一步一步地分解它。 😉

  • NPE JavaDoc明确地说“空对象的其他非法使用” 。 如果仅限于运行时遇到null的情况,那么所有这些情况都可以更简洁地定义。

  • 如果你承担了错误的事情,它不能帮助它,但假设封装应用正确,你真的不应该在意或注意null是否被不适当地解引用,而不是一个方法检测到一个不适当的null,并引发exceptionclosures。

  • 我会selectNPE而不是IAE ,原因有很多

    • 非法经营的性质更为具体
    • 错误地允许空值的逻辑往往与错误地允许非法值的逻辑非常不同。 例如,如果我validation用户input的数据,如果我得到的值是不可接受的,则该错误的来源是应用程序的最终用户。 如果我得到一个null,这是程序员错误。
    • 无效的值可能会导致堆栈溢出,内存不足错误,parsingexception等情况。实际上,大多数错误在某些方面通常performance为某些方法调用中的无效值。 出于这个原因,我将IAE看作是RuntimeException下的所有exception的常见的部分。
  • 其实,其他无效论据可能会导致其他各种例外。 UnknownHostException , FileNotFoundException ,各种语法错误exception, IndexOutOfBoundsException ,authentication失败等。

一般来说,我觉得NPE是非常恶意的,因为传统上,它与那些不遵循快速失败原则的代码相关联。 再加上JDK没有用消息string来填充NPE,实际上已经产生了一个很不好的负面情绪。 事实上,NPE和IAE在运行时间方面的区别是严格的名称。 从这个angular度来看,你对这个名字越精确,你给这个调用者就越清晰。

这是一个“圣战”风格的问题。 换句话说,两种select都是好的,但是人们会有自己的偏好,他们会为死亡辩护。

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

所以,如果你使用它,它是nullNullPointer 。 如果它被传入,它是nullIllegalArgument

Apache Commons Lang有一个NullArgumentExceptionexception ,它在这里讨论了许多事情:它扩展了IllegalArgumentException,它的唯一构造函数接受了非空的参数名称。

虽然我觉得抛出类似NullArgumentException或IllegalArgumentException的东西更准确地描述了特殊的情况,但是我和我的同事select了Bloch关于这个主题的build议。

被接受的做法,如果使用IllegalArgumentException(String消息)声明一个参数是无效的,并尽可能多的细节…所以说,一个参数被发现是空的,而exception非空,你会做一些喜欢这个:

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

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

不能同意所说的话。 提前失败,快速失败。 很不错的例外口头禅。

抛出哪个例外的问题大多是个人品味的问题。 在我看来,IllegalArgumentException似乎比使用NPE更具体,因为它告诉我,问题是我传递给方法的参数,而不是执行方法时可能产生的值。

我的2分

实际上,抛出IllegalArgumentException或者NullPointerException的问题在我看来只是一个less数派的“圣战”,对Java中的exception处理有着无法理解的理解。 一般来说,规则很简单,如下:

  • 参数约束违规必须尽可能快地指示( – >快速失败),以避免非常难以debugging的非法状态
  • 如果出于任何原因无效的空指针,抛出NullPointerException
  • 如果数组/索引非法,则抛出ArrayIndexOutOfBounds
  • 在数组/集合大小为负的情况下,抛出NegativeArraySizeException
  • 如果上面没有涉及的非法参数,并且没有其他更具体的exceptiontypes,则将IllegalArgumentException作为废品篮抛出
  • 另一方面,如果出于某种合理原因无法通过快速失败来避免违反WITHIN A FIELD的约束,则可以捕获并重新抛出IllegalStateException或更具体的检查exception。 在这种情况下永远不要传递原始的NullPointerException,ArrayIndexOutOfBounds等!

对于将各种参数约束违规映射到IllegalArgumentException的情况,至less有三个非常好的理由,第三个可能是如此严重以至于标记实践不良风格:

(1)程序员不能安全地假定所有的参数约束违例的情况都会导致IllegalArgumentException,因为大多数标准类使用这个exception,而不是一个废物篮,如果没有更多的特定types的exception可用。 尝试将所有的参数约束违例情况映射到API中的IllegalArgumentException,只会导致程序员因为使用类而感到沮丧,因为标准库大多遵循违反你的规则,而且大多数API用户也会使用它们!

(2)映射exception实际上会导致另一种exception,由单一inheritance引起:所有Javaexception都是类,因此仅支持单一inheritance。 因此,没有办法创build一个真正说NullPointerException和IllegalArgumentException的exception,因为子类只能从一个或另一个inheritance。 如果参数为null,则抛出IllegalArgumentException,从而使API用户难以在程序尝试以编程方式纠正问题时区分问题,例如通过将默认值提供给调用重复!

(3)映射实际上会产生bug掩盖的危险:为了将参数约束违例映射到IllegalArgumentException,需要在每个有任何约束参数的方法中编写一个外部try-catch。 然而,仅仅在这个catch块中捕获RuntimeException是不可能的,因为即使它们不是由参数约束违例引起的,那么风险也会将由在你的内部使用的libery方法抛出的logging的RuntimeException映射到IllegalArgumentException。 所以你需要非常具体,但即使这样,你也不能保护你不会将你另一个API的未公开的运行时exception(即一个bug)映射到你的API的IllegalArgumentException。 因此,即使是最仔细的映射,也会掩盖其他库制造商的编程错误,将其作为您的方法用户的参数约束违规行为,这只是一种令人讨厌的行为!

另一方面,标准的做法,规则保持简单,exception原因保持不被掩盖和具体。 对于方法调用者来说,规则也很简单: – 如果由于传递了非法值而遇到任何types的logging的运行时exception,则可以使用默认值重复调用(对于此特定的exception是必需的),或更正代码 – 另一方面,如果你在一个给定的参数集合中包含了一个没有logging的运行时exception,那么就向该方法的制造者提交一个错误报告,以确保他们的代码或者他们的文档是固定的。

我想从其他非法参数中选出Null参数,所以我从名为NullArgumentException的IAE派生了一个exception。 甚至不需要读取exception消息,我知道一个空参数被传递给一个方法,通过阅读消息,我发现哪个参数为空。 我仍然用IAE处理程序捕捉到NullArgumentException,但是在我的日志中,我可以很快地看到差异。

抛出一个null参数独占的exception(不pipe是NullPointerException还是一个自定义types)使得自动化的nulltesting更加可靠。 这个自动化testing可以通过reflection和一组默认值完成,就像Guava的NullPointerTester 。 例如, NullPointerTester将尝试调用以下方法…

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

…有两个参数列表: "", nullnull, ImmutableList.of() 。 它会testing每个这样的调用都抛出预期的NullPointerException 。 对于这个实现,传递一个null列表不会产生NullPointerException 。 但是,它确实会产生一个IllegalArgumentException因为NullPointerTester碰巧使用默认的""string。 如果NullPointerTester只对null值期望NullPointerException ,它会捕获该错误。 如果它期望IllegalArgumentException ,它会错过它。

有些集合假设使用NullPointerException而不是IllegalArgumentException来拒绝null 。 例如,如果将包含null的集合与拒绝null的集合进行比较,则第一个集合将调用另一个集合上的containsAll ,并捕获其NullPointerException – 但不包含IllegalArgumentException 。 (我正在查看AbstractSet.equals的实现。)

你可以合理地争辩说,以这种方式使用未经检查的exception是一种反模式,比较包含null的集合和不能包含null集合是一个真正应该产生exception的可能的错误,或者在集合中根本就是null一个坏主意。 尽pipe如此,除非你愿意说equals在这种情况下抛出一个exception,否则你会记住NullPointerException在某些情况下是必需的,而在其他情况下是不需要的。 (“NPE之前的除了'c'之后的IAE …”)

尝试访问具有当前值为空的引用variables的对象时抛出NullPointerException

IllegalArgumentException当方法接收格式不同于方法所期望的参数时抛出

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

这是我以前就职的内部政策的一部分,工作得很好。 这一切都来自记忆,所以我不记得确切的措辞。 值得注意的是,他们没有使用检查的exception,但这超出了问题的范围。 他们使用的未经检查的例外分为三大类。

NullPointerException:不要故意抛出。 当解引用空引用时,NPE将仅由VM引发。 所有可能的努力都是要确保这些都不会被抛出。 @Nullable和@NotNull应该与代码分析工具一起使用来查找这些错误。

IllegalArgumentException:当一个函数的参数不符合公共文档时,抛出错误,从而可以通过传入的参数来识别和描述错误.OP的情况将属于这个类别。

IllegalStateException:当一个函数被调用并且它的参数在传递的时候是意外的,或者是方法所属的对象的状态不兼容的时候抛出。

例如,在有长度的东西中使用了两个内部版本的IndexOutOfBoundsException。 一个IllegalStateException的子类,如果索引大于长度,则使用该子类。 另一个是IllegalArgumentException的子类,如果索引是负数,则使用该子类。 这是因为你可以添加更多的项目对象和参数将是有效的,而负数是永远无效的。

正如我所说,这个系统工作得很好,并且有人解释了为什么区别在那里:“根据错误的types,你很容易找出该怎么做,即使你实际上不能弄清楚出了什么问题,你可以找出在哪里发现错误,并创build更多的debugging信息。“

NullPointerException:处理Null大小写或放置断言,以便不引发NPE。 如果你提出一个断言,它必须是另外两种types之一。 如果可能的话,继续debugging,就好像断言是在那里。

IllegalArgumentException:您的呼叫站点有问题。 如果传入的值来自另一个函数,请找出为什么您收到的值不正确。 如果你传入一个参数传播错误,则检查调用堆栈,直到find没有返回所期望的函数。

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

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

一般来说,开发人员不应该抛出一个NullPointerException。 当代码尝试取消引用值为null的variables时,运行时会抛出此exception。 因此,如果你的方法想明确地拒绝null,而不是发生null值引发NullPointerException,你应该抛出一个IllegalArgumentException。

二分法…它们是不重叠的吗? 只有一个整体的非重叠部分才能成为一个二分法。 照我看来:

 throw new IllegalArgumentException(new NullPointerException(NULL_ARGUMENT_IN_METHOD_BAD_BOY_BAD)); 

根据你的情况, IllegalArgumentException是最好的select,因为null不是你的属性的有效值。

理想的运行时exception不应该被抛出。 应该为您的scheme创build检查的exception(业务exception)。 因为如果这些exception中的任何一个被抛出并被logging下来,那么在审查日志的时候就会误导开发者。 相反,业务exception不会造成这种恐慌,而且在排除故障时通常会被忽略。

上面两个例外的链接的定义是IllegalArgumentException:抛出指示一个方法已经通过了一个非法或不恰当的参数。 NullPointerException:在应用程序尝试在需要对象的情况下使用null时抛出。

The big difference here is the IllegalArgumentException is supposed to be used when checking that an argument to a method is valid. NullPointerException is supposed to be used whenever an object being "used" when it is null.

I hope that helps put the two in perspective.

If it's a "setter", or somewhere I'm getting a member to use later, I tend to use IllegalArgumentException.

If it's something I'm going to use (dereference) right now in the method, I throw a NullPointerException proactively. I like this better than letting the runtime do it, because I can provide a helpful message (seems like the runtime could do this too, but that's a rant for another day).

If I'm overriding a method, I use whatever the overridden method uses.

You should throw an IllegalArgumentException, as it will make it obvious to the programmer that he has done something invalid. Developers are so used to seeing NPE thrown by the VM, that any programmer would not immediately realize his error, and would start looking around randomly, or worse, blame your code for being 'buggy'.

In this case, IllegalArgumentException conveys clear information to the user using your API that the " should not be null". As other forum users pointed out you could use NPE if you want to as long as you convey the right information to the user using your API.

GaryF and tweakt dropped "Effective Java" (which I swear by) references which recommends using NPE. And looking at how other good APIs are constructed is the best way to see how to construct your 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.

If you choose to throw a NPE and you are using the argument in your method, it might be redundant and expensive to explicitly check for a null. I think the VM already does that for you.