利用 try catch 块而不是重复进行空检查

Taking advantage of try catch block instead of doing repeated null checks

今天我正在考虑从我的代码中删除不必要的 null 条件检查,主要涉及深层嵌套对象。 例如:

我有一个 class Big 并且它引用了 Small 对象及其 getter 和 setter,同样 Small 引用了 Tiny 对象及其 getter 和 setter 以及 Tiny 对象具有 tinyLength 变量,它是一个 Integer 变量。

因此,例如,如果我正在编写一个接受 Big 对象参数并且最后 returns tinyLength 的方法,我通常会做一些事情 像这样:

public Integer getTinyLength (Big big) {
    if (big!=null && big.getSmall()!=null && big.getSmall().getTiny()!=null &&
        big.getSmall().getTiny().getTinyLength()!=null) {
    return big.getSmall().getTiny().getTinyLength();
}
else {
    // throw exception or return null or do whatever
}

所以我的问题不是做所有这些垃圾,为什么我不做类似的事情:

try {
    return big.getSmall().getTiny().getTinyLength();
}    
catch (Exception e){
    //null pointer exception being a subclass of Exception class   
    log your error here 
    throw new NullPointerException();
}

我觉得第二个代码片段非常简洁明了。这样做有什么缺点吗? 特别是当有多个嵌套对象时,取消对每个嵌套对象的引用通常会导致代码高度不可读/如果您错过其中一个对象也容易出错,并导致空值。

第一个代码示例效率低下,并且线程不安全。 getSmall()被调用了四次,getTiny()被调用了三次,getTinyLength()被调用了两次。

第二个代码示例很危险。当您只想捕获 NullPointerException 时捕获 Exception 可能会导致捕获您不想捕获的异常。此外,构造一个 NullPointerException 对象、抛出它、捕获它、丢弃它以及创建另一个 NullPointerObject 的开销是非常重要的。

更好的是:

Integer length = null;
if (big != null) {
    Small small = big.getSmall();
    if (small != null) {
        Tiny tiny = small.getTiny();
        if (tiny != null) {
            length = tiny.getTinyLength();
        }
    }
}
if (length != null) {
    return length;
} else {
    // Throw exception, return null, or do whatever
}

更好的方法是将该代码隐藏在 Big#getTinyLength() 内的 getter 中。

认真查看您的数据模型应该是有序的。你能有一个没有 SmallBig 吗?你能有一个不包含 TinySmall 吗?为什么 Tiny 没有长度?如果这些成员 不应该 永远是 null,那么让 JVM 检查它并抛出异常,但不要捕获它,只是重新抛出一个等效的:

return big.getSmall().getTiny().getTinyLength();

编辑

如果您使用的是 Java 8,那么 Optional 类型可以简化您的代码:

public Integer getTinyLength (Big big) {
    return Optional.ofNullable(big)
        .map(Big::getSmall)
        .map(Small:getTiny)
        .map(Tiny::getTinyLength)
        .orElse(null);
}

这从 big 构造了一个 Optional<Big>,并继续展开它以获得微小的长度值。如果 bignull,或者如果 .map(...) 中使用的任何映射函数调用 returns null,则会生成一个空的可选值。最后,如果 Optional<Integer> 存在则返回值,否则返回 null