如何检查多个对象是否为空?

85

经常能看到以下代码结构:

if(a == null || b == null || c == null){
    //...
}

我想知道是否有任何广泛使用的库(如Google、Apache等)可以一次性检查多个对象是否为空,例如:

if(anyIsNull(a, b, c)){
    //...
}
或者
if(allAreNulls(a, b, c)){
    //...
}

更新:

  1. 我完全知道如何自己编写它。
  2. 我知道这可能是程序结构不良的结果,但这不是这种情况。
  3. 让我们将原始示例替换为类似于以下内容的东西,使其更具挑战性:

    if(a != null && a.getFoo() != null && a.getFoo().getBar() != null){
        //...
    }
    

更新2:

我已经为Apache Commons Lang库创建了一个拉请求来修复这个问题:

这些将被纳入commons-lang,版本3.5:

  • anyNotNull (Object... values)
  • allNotNull (Object... values)

5
写起来很容易,但我很少遇到这种需要。你是否考虑过你的数据可能结构不良? - Denys Séguret
1
如果你想要这种直接的空值检查,你可以通过传递列表并对其进行迭代来简单地创建实用程序。 - Panther
2
@Panther 一个列表?这里似乎更适合使用可变参数函数。 - Denys Séguret
@DenysSéguret 是的,那样会更好。 - Panther
3
从这种代码中我看到的一个问题是,你抛出的异常可能无法告诉你哪个参数为空而不应该为空的参数的名称。 - Sebastiaan van den Broek
显示剩余2条评论
8个回答

65

1
对我来说有点奇怪,因为它们有像 Objects 这样的类,当它们检查第一个非空对象时。但是没有一个检查所有或非空对象。 - Krzysztof Wolny
@KrzysztofWolny 是的,它们都是为处理单个可能为空的参数而设计的。我从未见过处理多个参数的方法,也不认为我需要它们。这可能是为什么我认为通用库不包括它们的一部分原因 - 需求可能存在,但可能不是很高。当然,每个程序员的工作方式都不同,所以这样的方法可能正是您所需的。请随意为您喜爱的库提出功能请求! - Petr Janeček
2
@Slantec会考虑这个建议,现在有了GitHub和它的pull requests,这变得非常容易 :) 而且Apache也在GH上有镜像。 - Krzysztof Wolny
1
实际上,Apache Commons Lang项目中已经有一个相关的票据了 :) https://issues.apache.org/jira/browse/LANG-781 - Krzysztof Wolny
1
@THISUSERNEEDSHELP 我仍然需要。这可能只是我的编码风格,但我喜欢尽早清除系统中的null,通过Preconditions.checkNotNull(something, "something")。当检查多个null时,我通常需要知道哪一个是null,这不能通过单个方法实现。这并不意味着我的方法从不返回null,它们有时会返回null。但总的来说,这是有限制的,因为我更喜欢其他结构。我认为在许多年里我没有使用过这样的方法,除了对象数组的情况。 - Petr Janeček
显示剩余2条评论

65
在Java 8中,您可以使用Stream.allMatch来检查所有值是否符合某个条件,例如是否为null。这并没有更短,但可能更容易阅读。
if (Stream.of(a, b, c).allMatch(x -> x == null)) {
    ...
}

对于anyMatchnoneMatch同样适用。


关于你的“更有挑战性的示例”:在这种情况下,我认为没有其他办法,只能编写惰性求值的空检查条件,并像你所写的那样:

if (a != null && a.getFoo() != null && a.getFoo().getBar() != null) {
    ...
}

使用流、列表或可变参数方法中的任何一种其他方法,都会尝试在测试 a 不为 null 之前评估 a.getFoo()。你可以使用 Optionalmap 和方法指针,这将按顺序惰性地进行评估,但是否更易读是有争议的,并且可能因情况而异(特别是对于较长的类名):

if (Optional.ofNullable(a).map(A::getFoo).map(B::getBar).isPresent()) {
    ...
}

Bar bar = Optional.ofNullable(a).map(A::getFoo).map(B::getBar).orElse(null);

另一种选择可能是尝试访问最内层的项,但我觉得这也不被认为是良好的做法:

try {
    Bar bar = a.getFoo().getBar();
    ...
catch (NullPointerException e) {
    ...
}

特别地,在访问该元素之后,这也将捕获任何其他NPEs--要么您必须仅在try中放置Bar bar = ...,并在try之后的另一个if块中放置所有其他内容,从而使可读性或简洁性降为零。


有些语言有安全导航运算符,但似乎Java不是其中之一。这种方式下,您可以使用类似a?.getFoo()?.getBar() != null的符号,其中如果anulla?.getFoo()将仅计算为null。但是您可以通过自定义函数和lambda来模拟此类行为,返回一个Optional或者只是一个值或null,具体取决于您的喜好:

public static <T> Optional<T> tryGet(Supplier<T> f) {
    try {
        return Optional.of(f.get());
    } catch (NullPointerException e) {
        return Optional.empty();
    }
}

Optional<Bar> bar = tryGet(() -> a.getFoo().getBar(););

12
哦,这实际上是一个不错的想法。此外,可以使用Objects::isNull作为谓词。 - Petr Janeček
1
不错,谢谢您提供的解决方案。不过我会用 Objects::isNull 替换 x -> x == null。在我看来更加简洁。 - Pieter De Bie
还有一个版本用于检查任何一个值是否为null。if(Stream.of(a, b, c).anyMatch(Objects::isNull)) - Justin
我发现需要通过对象内部结构挖掘值的需求有点像代码异味。为了避免这种挖掘,尝试让客户端以更原始的格式提供所需的值。比较:public void smell(A a) { if(a != null && a.getFoo() != null && a.getFoo().getBar() != null) ... }public void clean(Bar bar) { if(bar != null) ... } - Nadrendion

13
你也可以使用以下方法。它允许你传递任意数量的参数:

你也可以使用以下方法。它允许你传递任意数量的参数:

public static boolean isAnyObjectNull(Object... objects) {
    for (Object o: objects) {
        if (o == null) {
            return true;
        }
    }
    return false;
}

您可以使用任意数量的参数调用它:

isAnyObjectNull(a, b, c, d, e, f);
你可以对areAllNull做类似的事情。
public static boolean areAllObjectsNull(Object... objects) {
    for (Object o: objects) {
        if (o != null) {
            return false;
        }
    }
    return true;
}

注意:你也可以使用三元运算符而不是 if (o == null)。这里展示的两种方法都没有错误处理。请根据您的需要进行调整。


2
对于 areAllNull,如果 o != null,只需返回 false - tobias_k
@tobias_k:你是绝对正确的。我删除了我的评论并编辑了代码。 - kamwo
为什么你使用Boolean而不是原始的boolean类型? - AlexWien
@AlexWien:你可以使用任何你喜欢的(原始或对象)类型。根据你的需求更改方法。有时在应用程序中需要处理大量的布尔对象。通过使用 Boolean.FALSE / Boolean.TRUE,我获得了静态对象。这样整个系统中只存在两个对象,系统不必创建新对象。每次调用此方法或其他方法时,它都会返回对现有对象的引用。 - kamwo
1
@kamwo 当您使用原始类型时,系统也不必创建新的对象。 - Laurens Op 't Zandt
@LaurensOp'tZandt 我完全意识到这一点 :-) - kamwo

5

Objects.requireNonNull

通过使用Objects类及其requireNonNull方法,可以实现。

public static void requireNonNull(Object... objects) {
    for (Object object : objects) {
        Objects.requireNonNull(object);
    }
}

2
requireNonNull 抛出异常。当 null 不是一个异常情况,而是更适合用于强制执行契约时,它比测试可空性更好。 - schatten

3

自3.11版本起,Apache commons-lang3提供了方法ObjectUtils.allNull(Object... values)


该方法可用于检查传入的参数是否全部为null值。
ObjectUtils.allNull(obj1, obj2, obj3);

2

就是这么简单:

Stream.of(a,b,c).allMatch(Objects::nonNull)

0

我正在寻找一个解决方案,但我还没有将apache作为依赖项,因此为了使用allNonNull方法而添加它对我来说感觉很愚蠢。这是我的普通的java解决方案,使用Predicate#and() / Predicate#or() 就像这样:

private static boolean allNonNull(A a) {
    Predicate<A> isNotNull = Objects::nonNull; 
    Predicate<A> hasFoo = someA -> someA.foo != null;
    Predicate<A> hasBar = someA -> someA.foo.bar != null;
    return Optional.ofNullable(a)
        .filter(isNotNull.and(hasFoo.and(hasBar)))
        .isPresent();
}

注意:对于 anyNonNull,只需使用 or() 方法而不是 and() 方法。
调用时,将给出以下输出:
    System.out.println(isValid(new A(new Foo(new Bar())))); // true
    System.out.println(isValid(new A(new Foo(null)))); // false
    System.out.println(isValid(new A(null))); // false
    System.out.println(isValid(null)); // false

使用的类定义:

public static class A {
    public A(Foo foo) {
        this.foo = foo;
    }
    Foo foo;
}

public static class Foo {
    public Foo(Bar bar) {
        this.bar = bar;
    }
    Bar bar;
}

public static class Bar { }

-2
你可以创建一个对象列表并在其中使用 yourList.contains(null)
List < Object > obList = new ArrayList < Object > ();

String a = null;
Integer b = 2;
Character c = '9';

obList.add(a);
obList.add(b);
obList.add(c);

System.out.println("List is " + obList);

if (obList.contains(null)) {
    System.out.println("contains null");
} else {
    System.out.println("does not contains null");
}

演示


2
很好。或者,为了使它比原来的更短,可以使用 Arrays.asList(a, b, c).contains(null)。但是对于所有为空的情况不起作用... - tobias_k

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接