检查方法链中的最后一个getter是否为null

时间:2013-10-28 08:30:17

标签: java design-patterns

在代码中我们有很多链方法,例如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()); - 这将是最好的(我想是这样),但如何实现呢?

3 个答案:

答案 0 :(得分:7)

您可以使用Option模式获得所需的结果。这会强制您更改方法签名,但基本上如果您的方法返回某种类型T,它会保证它具有一些非空值,如果它返回Option<T>,则表示它具有值T,或null。

Java 7有一些称为null安全的功能,但它已从最终版本中删除。你可以这样做:

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

此外,Java 8将添加一个名为Optional的功能,因此您可以安全地执行此操作。

事实上,如果您现在真的想要使用它,请尝试Null Object模式。这意味着您可以返回某种默认值,而不是返回普通null,而不会触发NullPointerException。但是,您需要为getter添加一些更改

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;
   }
}

编辑:如果您想要实用程序,可以将其包装在某个功能界面中然后调用它。

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

用法如下:

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

不需要您更改现有功能

答案 1 :(得分:7)

从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类型here

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

答案 2 :(得分: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()调用返回null时,您仍然需要以某种方式处理错误,因此d变为null.最好的方法是返回一些默认值{{} 1}}在包装函数中。


如果任何中间D返回null,我不会看到您在问题末尾列出的两个选项将如何帮助;你会得到getX()