空检查链与捕获空点异常

Web服务返回一个巨大的XML,我需要访问它的深度嵌套字段。例如:

return wsObject.getFoo().getBar().getBaz().getInt()

问题是, ,可能全部返回。getFoo()getBar()getBaz()null

但是,如果我在所有情况下都检查代码,代码会变得非常冗长且难以阅读。此外,我可能会错过某些字段的检查。null

if (wsObject.getFoo() == null) return -1;
if (wsObject.getFoo().getBar() == null) return -1;
// maybe also do something with wsObject.getFoo().getBar()
if (wsObject.getFoo().getBar().getBaz() == null) return -1;
return wsObject.getFoo().getBar().getBaz().getInt();

是否可以接受编写

try {
    return wsObject.getFoo().getBar().getBaz().getInt();
} catch (NullPointerException ignored) {
    return -1;
}

或者这会被认为是一个反模式吗?


答案 1

捕捉是一件非常有问题的事情,因为它们几乎可以在任何地方发生。很容易从错误中获取一个,偶然发现它并继续好像一切正常,从而隐藏了一个真正的问题。处理起来非常棘手,因此最好完全避免。(例如,考虑自动取消装箱空值。NullPointerExceptionInteger

我建议您改用 Optional 类。当您想要使用存在或不存在的值时,这通常是最佳方法。

使用它,你可以像这样编写你的代码:

public Optional<Integer> m(Ws wsObject) {
    return Optional.ofNullable(wsObject.getFoo()) // Here you get Optional.empty() if the Foo is null
        .map(f -> f.getBar()) // Here you transform the optional or get empty if the Bar is null
        .map(b -> b.getBaz())
        .map(b -> b.getInt());
        // Add this if you want to return null instead of an empty optional if any is null
        // .orElse(null);
        // Or this if you want to throw an exception instead
        // .orElseThrow(SomeApplicationException::new);
}

为什么是可选的?

使用 s 而不是可能不存在的值会使该事实对读者非常明显和清晰,并且类型系统将确保您不会意外忘记它。Optionalnull

您还可以访问更方便地处理此类值的方法,例如 maporElse


缺席是有效的还是错误的?

但也要考虑中间方法返回 null 是否是有效结果,或者这是否是错误的征兆。如果它总是一个错误,那么它可能最好抛出一个异常,而不是返回一个特殊值,或者中间方法本身抛出一个异常。


也许有更多的可选选项?

另一方面,如果中间方法中缺少的值是有效的,也许您也可以为它们切换到s?Optional

然后你可以像这样使用它们:

public Optional<Integer> mo(Ws wsObject) {
    return wsObject.getFoo()
        .flatMap(f -> f.getBar())
        .flatMap(b -> b.getBaz())
        .flatMap(b -> b.getInt());        
}

为什么不是可选的?

我能想到的不使用的唯一原因是,如果这是代码中真正性能关键的部分,以及垃圾回收开销是否是一个问题。这是因为每次执行代码时都会分配一些对象,而 VM 可能无法优化这些对象。在这种情况下,您的原始 if-test 可能会更好。OptionalOptional


答案 2

我建议考虑 Objects.requireNonNull(T obj, String message)。您可以为每个异常构建带有详细消息的链,例如

requireNonNull(requireNonNull(requireNonNull(
    wsObject, "wsObject is null")
        .getFoo(), "getFoo() is null")
            .getBar(), "getBar() is null");

我建议您不要使用特殊的返回值,例如.这不是Java风格。Java设计了异常机制,以避免这种来自C语言的老式方式。-1

投掷也不是最好的选择。您可以提供自己的异常(对其进行检查以确保它将由用户处理,或者取消选中以更简单的方式处理它),或者使用您正在使用的 XML 解析器中的特定异常。NullPointerException