7

您是否知道Apache Commons ValidateGuava Preconditions的一些不错的替代方法,在检查对象是否不为空Spring Assert除外)时会抛出 IllegalArgumentException 而不是 NullPointerException ?


我知道Javadocs说:

应用程序应抛出此类 [NullPointerException] 的实例,以指示空对象的其他非法用途。

尽管如此,我只是不喜欢它。对我来说,NPE 总是意味着我只是忘记在某处保护空引用。我的眼睛受过如此训练,我可以发现它以每秒几页的速度浏览日志,如果我这样做了,我的脑海中总是会启用错误警报。因此,将它扔到我期望 IllegalArgumentException 的地方会让我很困惑。

假设我有一个 bean:

public class Person {
  private String name;
  private String phone;
  //....
}

和服务方法:

public void call(Person person) {
  //assert person.getPhone() != null
  //....
}

在某些情况下,一个人没有电话可能没问题(我奶奶没有任何电话)。但是如果你想打电话给这样的人,对我来说,它是调用带有IllegalArgument传递的call方法。查看层次结构 - NullPointerException 甚至不是 IllegalArgumentException 的子类。它基本上告诉您-您再次尝试在 null 引用上调用 getter

此外,已经进行了讨论,我完全支持这个很好的答案。所以我的问题是——我需要做这样丑陋的事情吗:

Validate.isTrue(person.getPhone() != null, "Can't call a person that hasn't got a phone");

以我的方式进行,或者是否有一个库会抛出 IllegalArgumentException 进行 notNull 检查?

4

7 回答 7

8

由于这个问题的主题演变为“IllegalArgumentException 和 NullpointerException 的正确用法”,我想指出有效 Java 第 60 条(第二版)中的直截了当的答案:

可以说,所有错误的方法调用都归结为非法参数或非法状态,但其他异常通常用于某些类型的非法参数和状态。如果调用者在某个参数中传递了 null 值,而该参数禁止使用 null 值,则约定规定抛出 NullPointerException 而不是 IllegalArgumentException。同样,如果调用者将表示索引的参数中的超出范围的值传递到序列中,则应抛出 IndexOutOfBoundsException 而不是 IllegalArgumentException。

于 2015-05-11T16:01:45.307 回答
5

Preconditions的呢checkArgument

public void call(Person person) {
    Preconditions.checkArgument(person.getPhone() != null);
    // cally things...
}

checkArgument throws IllegalArgumentException而不是NullPointerException.

于 2015-05-04T20:11:31.320 回答
1

您可以将 valid4j 与 hamcrest-matchers 一起使用(在 Maven Central 上以 org.valid4j:valid4j 的形式找到)。'Validation' 类支持常规输入验证(即抛出可恢复的异常):

import static org.valid4j.Validation.*;

validate(argument, isValid(), otherwiseThrowing(InvalidException.class));

链接:

附带说明:这个库还支持前置条件和后置条件(就像断言一样),如果需要,可以注册您自己的自定义全局策略:

import static org.valid4j.Assertive.*;

require(x, greaterThan(0)); // throws RequireViolation extends AssertionError
...
ensure(r, notNullValue()); // throws EnsureViolation extends AssertionError
于 2016-01-19T19:31:47.320 回答
1

看看https://github.com/cowwoc/requirements.java/(我是作者)。withException()您可以使用如下方式覆盖默认异常类型:

new Verifiers().withException(IllegalArgumentException.class).requireThat(name, value).isNotNull();
于 2016-10-07T16:39:51.447 回答
0

不是我知道的。我只是通过简洁的调用来实现你想要的行为,模仿 Guava 的实现,但调整异常类型。

class Preconditionz {
    public static <T> T checkNotNull(T reference, Object errorMessage) {
        if (reference == null) {
            throw new IllegalArgumentException(String.valueOf(errorMessage));
        }
        return reference;
    }
}

我喜欢继续import static这些非常常用的方法,所以你可以超级简洁地称呼它们。

import static com.whatever.util.Preconditionz.checkNotNull;

// ...

public void call(Person person) {
    checkNotNull(person, "person");
    checkNotNull(person.getPhone(), "person.phone");
    // ...
}

根据您的环境,您可能需要为其命名,checkNotNull2以便更容易通过 IDE 中的自动完成添加导入,或者让您将其与标准checkNotNull.

于 2015-05-04T20:30:36.233 回答
0

我想我在这里再次学到了一些东西,这要感谢 Olivier Grégoire、Louis Wasserman、CollinD 和 Captain Man 的精彩评论。这些标准通常是一个强有力的充分理由,因为它们使公共语言程序员总是能正确理解,但在这种特殊情况下,我有点怀疑,也许围绕 NPE 设置的这个规则不太好。Java 是一门古老的语言,它的一些特性有点不走运(我不想说错,这可能是太强的判断力)——比如检查异常,尽管你也可能不同意。现在我认为这个疑问已经解决了,我应该:

  • 在特定上下文中抛出 IllegalArgumentException 我可以从业务角度判断为什么 null 值是错误的。例如,在服务方法中,public void call(Person person)我知道电话号码为空对系统意味着什么。
  • 当我只知道这里的 null 值是错误的并且迟早会导致 NullPointerException 时抛出 NullPointerException,但在特定上下文中,我不知道从业务角度来看它意味着什么。例如 Guavas 不可变集合。当您构建此类并尝试添加一个 null 值的元素时,它会给您一个 NPE。它不明白这个值对你意味着什么,它太笼统了,但它只是知道它在这里是错误的,所以它决定也立即告诉你这个,并带有一些更合适的消息,这样你就可以更有效地识别问题。

考虑到上述情况,我想说在public void call(Person person)示例中做出断言的最佳选择就像曼船长建议的那样:

Preconditions.checkArgument(person.getPhone() != null, "msg");

Check 参数是这个方法的一个好名字——很明显,我正在根据 person 参数检查业务合同的合规性,很明显,如果它失败了,我会期待 IllegalArgumentException。它比 Apache Commons 的 Validate.isTrue 更好。另一方面,说 Validate.notNull 或 Preconditions.checkNotNull 表明我正在检查空引用并且我实际上期待 NPE。

所以最终的答案是——没有这么好的库,不应该这样,因为这会让人困惑。(并且应该更正 Spring Assert)。

于 2015-05-05T07:05:31.483 回答
-1

您可以轻松地做到这一点:

if (person.getPhone() == null) {
    throw new IllegalArgumentException("Can't call a person that hasn't got a phone");
}

其他程序员很清楚您的意思,并且完全按照您的意愿行事。

于 2015-05-04T20:06:53.757 回答