对于null参数,IllegalArgumentException或NullPointerException?

ddolor 发布于 2018-03-01 exception 最后更新 2018-03-01 01:01 1464 浏览

对于属性我有一个简单的setter方法,null不适用于这个特定的属性。我一直在这种情况下被撕毁:我应该抛出IllegalArgumentException还是NullPointerException?从javadocs来看,两者似乎都适合。是否有某种被理解的标准?或者,这只是其中一件事,你应该做任何你喜欢的事情,而且两者都是正确的?

已邀请:

ehic

赞同来自:

如果您不希望null成为允许的值,并且NullPointerException将在您尝试使用变量变为null时抛出,则似乎需要调用IllegalArgumentException

hvelit

赞同来自:

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

ased

赞同来自:

我倾向于遵循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");  
    }  
}
不管你做什么,不要让一个错误的值被设置,并在其他代码尝试使用它时抛出异常。这使得调试成为一场噩梦。你应该始终遵循“快速失败”的原则。

jneque

赞同来自:

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

fillum

赞同来自:

如果使用 IllegalArgumentException(String message)声明一个参数无效并给出尽可能多的细节,那么这种被接受的做法是......所以说,一个参数被发现是空的而异常不是 - 没有,你会做这样的事情:

if( variable == null )
    throw new IllegalArgumentException("The object 'variable' cannot be null");
您几乎没有理由隐式使用“NullPointerException”。 NullPointerException是当您尝试在空引用上执行代码(如 toString())时由Java虚拟机抛出的异常。

ehic

赞同来自:

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

"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."

tin

赞同来自:

上面两个例外的链接的定义是 IllegalArgumentException:抛出以指示某个方法已通过非法或不恰当的参数。 NullPointerException:应用程序尝试在需要对象的情况下使用null时抛出。 这里最大的区别是IllegalArgumentException在检查方法的参数是否有效时应该使用。当空对象被“使用”时,应该使用NullPointerException。 我希望这有助于两个角度。

uaut

赞同来自:

不能同意所说的话。提前失败,快速失败。很不错的例外口头禅。 关于抛出哪个例外主要是个人品味的问题。在我看来,IllegalArgumentException似乎比使用NPE更具体,因为它告诉我问题出在我传递给方法的参数上,而没有在执行方法时可能产生的值。 我的2分

siusto

赞同来自:

如果它是一个“setter”,或者稍后我会得到一个成员,我倾向于使用IllegalArgumentException。 如果我现在要在方法中使用(取消引用),我会主动抛出一个NullPointerException。我喜欢这样比让运行时更好,因为我可以提供一个有用的信息(看起来运行时也可以这样做,但这是另一天的咆哮)。 如果我重写一个方法,我使用任何重写的方法使用。

funde

赞同来自:

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

bdicta

赞同来自:

由于以下原因,您应该使用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并不足以胜过这些其他考虑因素。

gqui

赞同来自:

你应该抛出一个IllegalArgumentException,因为它会让程序员明白他做了一些无效的事情。开发人员很习惯看虚拟机抛出的NPE,任何程序员都不会马上意识到自己的错误,并会随机开始四处查看,或者更糟糕的是,应该将自己的代码归咎于“错误”。

tut

赞同来自:

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

funde

赞同来自:

在这种情况下,IllegalArgumentException将使用API​​的明确信息传递给用户“不应该为null”。正如其他论坛用户指出,只要您使用API​​将正确的信息传达给用户,就可以使用NPE。 GaryF和tweakt放弃了“Effective Java”(我发誓)推荐使用NPE的参考文献。看看如何构建其他良好的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(消息),然后在该组织中捕获该IllegalArgumentException(消息)。 springframework.beans.BeanUtils.instantiateClass(...)方法。

rvero

赞同来自:

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

veos

赞同来自:

Apache Commons Lang有一个NullArgumentException,它在这里讨论了许多事情:它扩展了IllegalArgumentException,它的唯一构造函数接受了应该是非空的参数的名称。 尽管我觉得抛出类似NullArgumentException或IllegalArgumentException的东西更准确地描述了特殊情况,但我和我的同事们选择了遵循Bloch关于此主题的建议。

sut

赞同来自:

杰森科恩的观点很好,因为它的表现很好。让我一步一步地分解它。 ;-)

  • NPE JavaDoc明确指出“空对象的其他非法使用”。如果它仅限于运行时遇到null的情况,那么所有这些情况都可以更简洁地定义。
  • 如果你承担了错误的事情,它不能帮助它,但假设封装应用正确,你真的不应该关心或注意null是否被不适当地解引用与方法是否检测到不适当的null并且关闭异常
  • 由于多种原因,我会在IAE上选择NPE
    • 关于非法行为的性质更具体。
    • 错误地允许空值的逻辑与错误地允许非法值的逻辑非常不同。例如,如果我验证用户输入的数据,如果我得到的值不可接受,则该错误的来源是应用程序的最终用户。如果我得到一个null,那是程序员错误。
    • 无效的值可能会导致堆栈溢出,内存不足错误,解析异常等情况。事实上,大多数错误通常在某些方法中作为某些方法调用中的无效值出现。出于这个原因,我将IAE视为RuntimeException下所有异常的最常见
    • 实际上,其他无效参数可能会导致其他各种异常。 UnknownHostExceptionFileNotFoundException,各种语法错误异常,IndexOutOfBoundsException,认证失败等等。 一般来说,我觉得NPE是非常恶意的,因为传统上与不遵循fail fast principle的代码相关联。除此之外,JDK未能用消息字符串填充NPE,实际上已经产生了一个强大的负面情绪,而这种情绪没有充分的根据。实际上,NPE和IAE之间从运行时间角度来看是完全不同的名称。从这个角度来看,你使用这个名称的准确性越高,你给调用者的清晰度就越高。

iullam

赞同来自:

我一致赞成抛出IllegalArgumentException作为null参数,直到今天,当我注意到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的做事方式”。 我认为我决定无论如何。 请注意,有关硬调试的参数是虚假的,因为您当然可以向NullPointerException提供一条消息,说明什么是空的,为什么它不应该为空。就像IllegalArgumentException一样。 NullPointerException的一个附加优势是,在高性能的关键代码中,您可以免除对空值的明确检查(并且NullPointerException带有友好的错误消息),并且只需要在调用方法时自动获得的NullPointerException在null参数上。如果您快速调用某个方法(即快速失败),那么您的效果基本相同,但对于开发人员而言并不像用户友好。大多数情况下,最好是明确地检查并抛出一个有用的消息来指示哪个参数为null,但如果性能决定不违反方法/构造函数的已发布合同,则可以选择更改该选项。

dsit

赞同来自:

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

throw new IllegalArgumentException(new NullPointerException(NULL_ARGUMENT_IN_METHOD_BAD_BOY_BAD));

modit

赞同来自:

有些馆藏假设使用NullPointerException而不是IllegalArgumentException来拒绝null。例如,如果将包含null的集合与拒绝null的集合进行比较,那么第一个集合将调用containsAll,并捕获其NullPointerException - 但不是IllegalArgumentException。 (我正在查看AbstractSet.equals的实现。) 您可以合理地争辩说,以这种方式使用未经检查的异常是一种反模式,即将包含null的集合与不能包含null的集合进行比较是真正产生异常的可能错误,一个集合中的null是一个坏主意。尽管如此,除非你愿意说equals应该在这种情况下抛出异常,否则你会记住NullPointerException在某些情况下是必需的,但在其他情况下不需要。 (“NPE之前除了'c'之后的IAE ......”)

modit

赞同来自:

抛出null参数专用的异常(无论是NullPointerException还是自定义类型)都会使null自动测试更加可靠。这种自动化测试可以使用反射和一组默认值完成,如在GuavaNullPointerTester中。例如,NullPointerTester会尝试调用以下方法...

Foo(String string, List<?> list) {
  checkArgument(string.length() > 0);
  // missing null check for list!
  this.string = string;
  this.list = list;
}
...有两个参数列表:"", nullnull, ImmutableList.of()。它会测试每个这样的调用都会抛出预期的NullPointerException。对于此实现,传递null列表并非
生成NullPointerException。但是,它确实产生了IllegalArgumentException,因为NullPointerTester碰巧使用默认的""字符串。如果NullPointerTester仅针对null值期望NullPointerException,则会捕获该错误。如果它期望IllegalArgumentException,它会错过它。

nnihil

赞同来自:

实际上,抛出IllegalArgumentException或NullPointerException的问题在我的简陋视图中只是对少数人的“神圣战争”,对Java中的异常处理有着无法理解的理解。一般来说,规则很简单,如下所示:

  • 参数约束违规必须尽可能快地指示( - >快速失败),以避免非常难以调试的非法状态
  • 出于任何原因无效空指针的情况下,抛出NullPointerException
  • 如果数组/索引非法,请抛出ArrayIndexOutOfBounds
  • 如果数组或集合大小为负数,则抛出NegativeArraySizeException
  • 如果非法参数未被上述覆盖,并且您没有其他更具体的异常类型,则将IllegalArgumentException作为垃圾箱抛出
  • 另一方面,如果出于某种有效原因无法通过快速失败避免违反WITHIN A FIELD的约束,则会捕获并重新抛出IllegalStateException或更具体的检查异常。在这种情况下,千万不要传递原始的NullPointerException,ArrayIndexOutOfBounds等!
  • 对于将各种参数约束违规映射为IllegalArgumentException的情况,至少有三个非常好的理由,第三种情况可能非常严重以至于标记练习不良风格: (1)程序员不能安全地假设所有参数约束违例的情况都会导致IllegalArgumentException,因为大多数标准类使用此异常而不是废物篮,如果没有更多的特定种类的异常可用的话。试图在API中将参数约束违例的所有情况映射到IllegalArgumentException只会导致程序员无法使用类,因为标准库大多遵循违反您的不同规则,并且大多数API用户也会使用它们! (2)映射异常实际上会导致不同类型的异常,这是由单一继承引起的:所有Java异常都是类,因此仅支持单一继承。因此,无法创建一个真正说NullPointerException和IllegalArgumentException的异常,因为子类只能从一个或另一个继承。在出现null参数的情况下抛出IllegalArgumentException因此使得API用户难以区分每次程序尝试以编程方式纠正问题时的问题,例如通过将默认值提供给呼叫重复! (3)映射实际上会产生bug掩盖的危险:为了将参数约束违例映射到IllegalArgumentException,您需要在每个有任何约束参数的方法中编写一个外部try-catch。但是,仅仅捕获此catch块中的RuntimeException是不成问题的,因为即使它们不是由参数约束违例引起的,那么该风险会将在您的内部使用的libery方法抛出的记录的RuntimeException映射到IllegalArgumentException。因此,您需要非常具体,但即使您不小心将未记录的另一个API(即错误)的运行时异常映射到您的API的IllegalArgumentException,也无法保护您。因此,即使是最仔细的映射也会掩盖其他库制造商的编程错误,因为这会影响您的方法用户的参数约束违规行为,这只是一种令人讨厌的行为! 另一方面,在标准实践中,规则保持简单,并且异常会导致不被屏蔽和具体。对于方法调用者来说,规则也很容易: - 如果您因为传递了非法值而遇到任何形式的记录的运行时异常,请使用默认值重复调用(对于此特定的异常是必需的),或更正您的代码 - 另一方面,如果你包含一个运行时异常,而这个异常没有记录为给定的一组参数发生,则向该方法的制造者提交一个错误报告,以确保他们的代码或其文档是固定的。

reum

赞同来自:

根据你的情况,IllegalArgumentException是最好的选择,因为null对你的财产不是有效的价值。

prem

赞同来自:

尝试访问具有当前值为空的引用变量的对象时抛出NullPointerException IllegalArgumentException当方法接收格式不同于方法所期望的参数时抛出

gautem

赞同来自:

理想情况下,不应抛出运行时异常。应该为您的方案创建检查的异常(业务异常)。因为如果这些异常中的任何一个被抛出并记录下来,它会在审阅日志时误导开发人员。相反,业务异常不会造成这种恐慌,并且在排除故障时通常会被忽略。

ased

赞同来自:

作为一个主观的问题,这应该是封闭的,但它仍然是开放的: 这是我以前就职的内部政策的一部分,它的工作非常好。这全是来自记忆,所以我不记得确切的措辞。值得注意的是,他们没有使用检查过的异常,但这超出了问题的范围。他们所使用的未经检查的例外分为三大类。 NullPointerException:不要故意抛出。当解引用空引用时,NPE将仅由VM引发。所有可能的努力都是要确保这些永远不会被抛出。 @Nullable和@NotNull应该与代码分析工具一起使用来查找这些错误。 IllegalArgumentException:当函数的参数不符合公共文档时发生,从而可以根据传入的参数来识别和描述错误.OP的情况将属于这一类。 IllegalStateException:当一个函数被调用并且它的参数在它们被传递的时候是意外的,或者与该方法所属的对象的状态不兼容时抛出。 例如,在有长度的东西中使用了两个内部版本的IndexOutOfBoundsException。一个IllegalStateException的子类,用于索引大于长度的情况。另一个是IllegalArgumentException的子类,如果索引是负数,则使用该子类。这是因为你可以添加更多的项目对象和参数将是有效的,而负数是永远无效的。 正如我所说,这个系统工作得很好,并且有人解释了为什么区别在那里:“根据错误的类型,你很容易找出该怎么做,即使你实际上不能弄清楚出了什么问题,你可以找出在哪里发现错误并创建更多的调试信息。“ NullPointerException:处理Null大小写或放入断言,以便不引发NPE。如果你输入一个断言,它必须是另外两种类型之一。如果可能的话,继续调试,就好像首先有断言一样。 IllegalArgumentException:您的调用网站出现问题。如果传入的值来自另一个函数,请找出您收到错误值的原因。如果你传入一个参数传播错误,则检查调用堆栈,直到找到没有返回所期望的函数。 IllegalStateException:你没有以正确的顺序调用你的函数。如果您正在使用其中一个参数,请检查它们并抛出一个描述该问题的IllegalArgumentException。然后,您可以将脸颊向上叠放,直到找到问题。 无论如何,他的观点是你只能将非法论点断言复制到堆栈中。您无法向堆栈中传播IllegalStateExceptions或NullPointerExceptions,因为它们与您的函数有关。