我有一个简单的属性设置方法,对于这个特定的属性,null
是不合适的。在这种情况下,我一直很犹豫:是抛出 IllegalArgumentException
,还是抛出 NullPointerException
?从 javadocs 中来看,两者似乎都可以。是否有某种明确的标准?或者这只是其中一种情况,你应该按照自己的喜好去做,而且两种做法都是正确的呢?
我有一个简单的属性设置方法,对于这个特定的属性,null
是不合适的。在这种情况下,我一直很犹豫:是抛出 IllegalArgumentException
,还是抛出 NullPointerException
?从 javadocs 中来看,两者似乎都可以。是否有某种明确的标准?或者这只是其中一种情况,你应该按照自己的喜好去做,而且两种做法都是正确的呢?
IllegalArgumentException
(IAE) 而不是 NullPointerException
(NPE),原因如下:null
时由运行时抛出的。相比之下,IAE JavaDoc 更加明确:“表示方法已收到一个不合法或不适当的参数。”没错,就是你!null
进行了解引用。当你看到 IAE 时,你会假设堆栈顶部方法的调用者传递了非法值。再一次,后面的假设是正确的,前面的假设是误导性的。null
如此特别,以至于它值得作为所有其他类型非法参数的单独异常?如果您不希望null
成为可接受的值,那么可能会出现IllegalArgumentException
。如果您尝试使用一个变量,但它是null
,则会抛出NullPointerException
异常。
IllegalArgumentException
与Java的Objects.requireNonNull(T)和Guava的Preconditions.checkNotNull(T)不一致,后者会抛出NullPointerException
。但是,正如Jason Cohen的优秀回答及其评论部分中所解释的那样,“正确答案”肯定是使用IllegalArgumentException
。 - matoni标准做法是抛出 NullPointerException
异常。通常可靠的《Effective Java》在第一版的Item 42、第二版的Item 60或第三版的Item 72“优先使用标准异常”中简要讨论了这个问题:
“可以说,所有错误的方法调用都归结为非法参数或非法状态,但其他异常通常用于特定类型的非法参数和状态。如果调用方在某个禁止使用null值的参数中传递了 null 值,惯例规定应该抛出 NullPointerException 而不是 IllegalArgumentException。”
我一直认为对于空参数应该抛出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的做法"。
无论如何,我想我已经决定了。
请注意,有关难以调试的论点是虚假的,因为您当然可以向NullPointerException
提供消息,说明什么为空以及为什么不应为空。就像使用IllegalArgumentException
一样。
NullPointerException
的另一个优点是,在高度性能关键的代码中,您可以省略对空值的显式检查(和带有友好错误消息的NullPointerException
),而只依赖于在null参数上调用方法时自动获取的NullPointerException
。只要快速调用方法(即快速失败),则基本上具有相同的效果,只是对开发人员来说不太友好。 大多数情况下,最好明确检查并使用有用的消息抛出以指示哪个参数为空,但是如果性能要求而不会破坏方法/构造函数的公布合同,则有更改该选项的选择。
Preconditions.checkNotNull(arg)
也会抛出NPE异常。 - assylias我倾向于遵循JDK库的设计,特别是Collections和Concurrency(Joshua Bloch、Doug Lea这些人知道如何设计可靠的API)。然而,JDK中许多API会主动抛出NullPointerException
。
例如,Map.containsKey
的Javadoc说明如下:
@throws NullPointerException 如果键是null并且此映射不允许null键(可选)。
抛出自己的NPE是完全有效的。惯例是在异常消息中包括为null
的参数名称。
模式如下:
public void someMethod(Object mustNotBeNull) {
if (mustNotBeNull == null) {
throw new NullPointerException("mustNotBeNull must not be null");
}
}
无论做什么,都不要允许设置错误的值,并在其他代码尝试使用它时稍后抛出异常。这会使调试成为一场噩梦。您应始终遵循“快速失败”原则。赞同Jason Cohen的观点,因为他表达得很清楚。让我逐步分解它。;-)
NPE JavaDoc明确表示“其他非法使用null对象”。如果仅限于运行时遇到不应该出现的null的情况,所有这些情况都可以更简洁地定义。
如果您假设封装被正确应用,那么您就不必在意或注意错误地取消引用null和方法是否检测到不当null并引发异常。
实际上,其他无效参数可能导致各种其他异常。 UnknownHostException、FileNotFoundException、各种语法错误异常、IndexOutOfBoundsException、身份验证失败等等。
总的来说,我认为NPE很不幸,因为传统上它与未能遵循快速失败原则的代码相关联。加上JDK未能用消息字符串填充NPE,确实创建了一种强烈的负面情绪,这种情绪并不是建立在良好基础上的。事实上,从运行时角度看,NPE和IAE之间的区别仅仅是名称。从这个角度来看,你对名称越精确,就越能给调用者提供明确的信息。
这是一个“圣战”风格的问题。换句话说,两个选择都不错,但人们会有自己的偏好,并且会为之而战。
NullPointerException
:这是JDK使用的惯例,也要求在接口中使用,它更具体(就像 IndexOutOfBoundsException
等),等等。 - Solomon Ucko如果这是一个setter
方法并且传入了null
,那么抛出IllegalArgumentException
会更合理。在你试图使用null
的情况下,NullPointerException
似乎更合适。
因此,如果您正在使用它且为null
,则使用NullPointer
。如果它被传入并且为null
,则使用IllegalArgument
。
Apache Commons Lang拥有一个NullArgumentException,它可以做讨论中提到的一些事情:它扩展了IllegalArgumentException,其唯一的构造函数需要传入未应为null的参数名称。
虽然我认为像NullArgumentException或IllegalArgumentException这样抛出异常更准确地描述了异常情况,但是我和同事们选择遵循Bloch在这个问题上的建议。
至少有三个很好的理由反对将所有种类的参数约束违规映射到IllegalArgumentException,第三个理由可能是如此严重,以至于标记该做法为不良风格:
(1)程序员不能安全地假设所有的参数约束违规都会导致IllegalArgumentException,因为大多数标准类使用此异常作为废纸篓,如果没有更具体的异常类型可用。试图在API中将所有的参数约束违规映射到IllegalArgumentException只会导致使用您的类的程序员感到沮丧,因为标准库大多遵循不同的规则,违反了您的规则,而且大多数API用户也会使用它们!
(2)映射异常实际上会导致一种不同种类的异常,这是由单继承引起的:所有Java异常都是类,因此只支持单继承。因此,无法创建既是NullPointerException又是IllegalArgumentException的异常,因为子类只能继承一个。因此,在空参数的情况下抛出IllegalArgumentException,这使得API用户更难以区分问题,例如通过将默认值馈入调用重复来编程地修正问题!
(3)映射实际上会产生掩盖错误的危险:为了将参数约束违规映射到IllegalArgumentException,您需要在每个具有任何约束参数的方法中编写外部try-catch。然而,在此catch块中简单地捕获RuntimeException是不可能的,因为那会将由您的方法中使用的库方法抛出的已记录RuntimeException映射到IllegalArgumentException,即使它们不是由参数约束违规引起的。因此,您需要非常具体,但即使那样的努力也不能保护您免受意外将另一个API的未记录运行时异常(即错误)映射到您的API的IllegalArgumentException的情况。即使是最小心的映射也会冒着将其他库制造商的编程错误掩盖为您方法的用户的参数约束违规的风险,这只是可笑的行为!
与标准做法相比,规则更简单,异常原因更明确。对于方法调用者而言,规则也很简单: - 如果遇到任何已记录的运行时异常,因为您传递了非法值,请使用默认值重复调用(对于这些特定的异常是必要的),或者纠正您的代码。 - 另一方面,如果您遇到一个未记录在案的运行时异常,针对给定的参数集合,向该方法的制造商提交错误报告,以确保他们的代码或文档得到修复。
Validate.notNull
(commons lang)和Preconditions.checkNotNull
(guava)都会抛出NPE :-( - Aaron DigullacheckArgument(arg!= null)
,只是没有它返回参数的便利性,或者您可以为您的项目创建一个本地实用程序。” http://code.google.com/p/guava-libraries/wiki/IdeaGraveyard - Arto Bendiken