检查“get”调用链是否为 null

2022-08-31 14:07:45

假设我想执行以下命令:

house.getFloor(0).getWall(WEST).getDoor().getDoorknob();

为了避免NullPointerException,如果出现以下情况,我必须执行以下操作:

if (house != null && house.getFloor(0) && house.getFloor(0).getWall(WEST) != null
  && house.getFloor(0).getWall(WEST).getDoor() != null) ...

有没有一种方法或已经存在的Utils类可以更优雅地做到这一点,让我们说下面这样的话?

checkForNull(house.getFloor(0).getWall(WEST).getDoor().getDoorknob());

答案 1

如果您无法避免违反所选答案中所述的 Demeter 定律 (LoD),并且随着 Java 8 引入 Optional,那么在像您这样的 get 链中处理 null 可能是最佳实践。

该类型将使您能够通过管道传输一行中的多个映射操作(其中包含 get 调用)。空检查在引擎盖下自动处理。Optional

例如,当对象未初始化时,不会创建 print(),也不会引发异常。这一切都在引擎盖下轻轻地处理。初始化对象时,将进行打印。

System.out.println("----- Not Initialized! -----");

Optional.ofNullable(new Outer())
        .map(out -> out.getNested())
        .map(nest -> nest.getInner())
        .map(in -> in.getFoo())
        .ifPresent(foo -> System.out.println("foo: " + foo)); //no print

System.out.println("----- Let's Initialize! -----");

Optional.ofNullable(new OuterInit())
        .map(out -> out.getNestedInit())
        .map(nest -> nest.getInnerInit())
        .map(in -> in.getFoo())
        .ifPresent(foo -> System.out.println("foo: " + foo)); //will print!

class Outer {
    Nested nested;
    Nested getNested() {
        return nested;
    }
}
class Nested {
    Inner inner;
    Inner getInner() {
        return inner;
    }
}
class Inner {
    String foo = "yeah!";
    String getFoo() {
        return foo;
    }
}

class OuterInit {
    NestedInit nested = new NestedInit();
    NestedInit getNestedInit() {
        return nested;
    }
}
class NestedInit {
    InnerInit inner = new InnerInit();
    InnerInit getInnerInit() {
        return inner;
    }
}
class InnerInit {
    String foo = "yeah!";
    String getFoo() {
        return foo;
    }
}

因此,使用您的 getters 链,它将如下所示:

Optional.ofNullable(house)
        .map(house -> house.getFloor(0))
        .map(floorZero -> floorZero.getWall(WEST))
        .map(wallWest -> wallWest.getDoor())
        .map(door -> wallWest.getDoor())

它的返回将是类似的,这将使您更安全地工作,而不必担心空异常。Optional<Door>


答案 2

为了检查一连串的 get 是否为 null,您可能需要从闭包中调用代码。闭包调用代码将如下所示:

public static <T> T opt(Supplier<T> statement) {       
    try {
        return statement.get();
    } catch (NullPointerException exc) {
        return null;
    }   
}

您可以使用以下语法调用它:

Doorknob knob = opt(() -> house.getFloor(0).getWall(WEST).getDoor().getDoorknob());

此代码也是类型安全的,并且通常按预期工作:

  1. 如果链中的所有对象都不为 null,则返回指定类型的实际值。
  2. 如果链中的任何对象为 null,则返回 null

您可以将 opt 方法放入共享 util 类中,并在应用程序中的任何位置使用它。