在代码中我们有很多链方法,例如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()
进行检查。为此目的,最佳解决方案是什么?
我认为最好的将是:
obj.getA().getB().getC().getD().isNull()
- 为此我需要更改所有的getter,例如实现一些包含isNull()
方法的接口。NullObjectHelper.isNull(obj.getA().getB().getC().getD());
- 这将是最好的(我想是这样),但如何实现呢?答案 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.isPresent和Optional.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);
没有方法可能比嵌套的 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()
。