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

pep*_*uch 8 java design-patterns

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

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

或者(这是"愚蠢的")

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

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

我认为最好的将是:

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

McD*_*ell 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();
Run Code Online (Sandbox Code Playgroud)

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

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

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

有关示例实现,请在此处查看Nullifier类型.

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


mis*_*off 7

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

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

obj?.getA()?.getB()?.getC()?.getD()
Run Code Online (Sandbox Code Playgroud)

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

事实上,如果你现在真的想使用它,试试Null Object模式.这意味着null您可以返回某种默认值而不是返回plain ,而不会触发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;
   }
}
Run Code Online (Sandbox Code Playgroud)

编辑:如果你想要实用程序,你可以将它包装在一些功能界面,然后调用它.

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

用法如下:

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

它不需要您更改现有功能