8

在代码中,我们有很多链方法,例如obj.getA().getB().getC().getD(). 我想创建帮助类来检查方法getD()是否不为空,但在此之前我需要检查所有以前的 getter。我可以这样做:

try {
    obj.getA().getB().getC().getD();
}
catch (NullPointerException e) {
    // some getter is null
}

或(这是“愚蠢的”)

if (obj!null && obj.getA()!=null && obj.getA().getB()!=null && ...) {
    obj.getA().getB().getC().getD();
}
else {
    // some getter is null
}

我不想每次try{} catch()在我的代码中使用时都检查它。为此目的的最佳解决方案是什么?

我认为最好的将是:

  1. obj.getA().getB().getC().getD().isNull()- 为此,我将需要更改我所有的 getter,例如实现一些包含isNull()方法的接口。
  2. NullObjectHelper.isNull(obj.getA().getB().getC().getD());- 这将是最好的(我认为是这样)但是如何实现呢?
4

3 回答 3

8

从 Java 8 开始,您可以使用Optional.isPresentOptional.orElse等方法来处理 getter 链中的 null :

boolean dNotNull = Optional.ofNullable(obj)
              .map(Obj::getA)
              .map(A::getB)
              .map(B::getC)
              .map(C::getD)
              .isPresent();

虽然这比捕获NullPointerException更可取,但这种方法的缺点是Optional实例的对象分配。

可以编写自己的静态方法来执行类似的操作,而不会产生这种开销:

boolean dNotNull = Nulls.isNotNull(obj, Obj::getA, A::getB, B::getC, C::getD);

有关示例实现,请参阅此处的Nullifier类型。

没有任何方法可能比嵌套的 if-not-null检查具有更高的运行时效率。

于 2015-03-17T21:10:19.830 回答
7

You can achieve the desired result with Option pattern. This enforces you to change a method signature, but basically if your method returns some type T, it guarantees it has some non-null value, and if it returnsOption<T> it means it either has value T, or null.

Java 7 had some feature called null safety, but it was removed from the final release. You could do:

obj?.getA()?.getB()?.getC()?.getD()

Moreover, Java 8 will add a feature called Optional so you would do it safely.

In fact, if you really want to use that now, try Null Object pattern. It means that instead of returning plain null you can return some sort of default value, which won't trigger NullPointerException. Though, you need add some changes to your getters

class Object {
   A getA() {
     // ...
     return a == null ? A.NULL : a;
   }
}

class A {
   static A NULL = new A(); // some default behaviour
   B getB() {
     if (this == NULL) return B.NULL;
     // ...
     return b == null ? B.NULL : b;
   }
}

EDIT: If you want utility to do it you can wrap it in some functional interface and then call it.

static boolean isNullResult(Callable call) throws Exception {
    try {
        return call.call() == null;
    } catch (NullPointerException npe) {
        return true;
    }
}

Usage will be the following:

isNullResult(new Callable<Integer>() {
    @Override
    public Integer call() throws Exception {
        return new A().getB().getC().getInt();
    }
});

It won't require you to change existing functionality

于 2013-10-28T10:25:29.180 回答
1

如前所述,真正的解决方案是重构。

同时,您可以将您的第一个解决方法包装在一个函数中:

static D getD(MyClass obj) {

    try {
        return obj.getA().getB().getC().getD();
    }
    catch (NullPointerException e) {
        return null; // Or even better, some default D
    }
}

在调用方站点:

D d = getD(obj);

至少您不必使用 try-catch 块来丢弃调用者。你仍然需要以某种方式处理错误,当一些中间getX()调用返回 a时null,最好是在包装函数中返回一些默认值。dnull.D


getX()如果任何中间人返回空值,我看不出您在问题末尾列出的两个选项有什么帮助;你会得到一个NullPointerException.

于 2013-10-28T11:17:12.517 回答