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

假设我想执行以下命令:

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()); 

最好的方法是避免链条。 如果您不熟悉得墨忒耳法(LoD),我认为您应该这样做。 你已经给出了一个完美的消息链示例,它与那些没有业务知识的类过于亲密。

德米特定律: http : //en.wikipedia.org/wiki/Law_of_Demeter

如果您无法避免违反所选答案中所述的Demeter法则(LoD),并且Java 8引入了Optional ,那么处理像你这样的获取链中的空值可能是最好的做法。

Optional类型将允许您连续管道多个映射操作(包含get调用)。 无效检查会在引擎盖下自动处理。

例如,如果未初始化对象,则不会生成print(),也不会抛出exception。 这一切都在引擎盖下轻轻处理。 初始化对象时,将进行打印。

 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 ,它可以让您更安全地工作而不必担心空例外。

为了检查获取链的null,您可以从闭包中调用您的代码。 闭包代码如下所示:

 public static  T opt(Supplier 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类中,并在应用程序的任何位置使用它。

你当然可以简单地将整个表达式包装在try-catch块中,但这是一个坏主意。 更清洁的是Null Object模式。 有了这个,如果你的房子没有0楼,它只会返回一个像普通楼层一样的楼层,但没有真正的内容; 当被问到他们没有的墙壁时,地板会返回类似的“空”墙等。

确保不能在逻辑上为null东西不是。 例如 – 房子总是有一堵西墙。 为了避免状态中的此类exception,您可以使用方法来检查您期望的状态是否存在:

 if (wall.hasDoor()) { wall.getDoor().etc(); } 

这基本上是一个空检查,但可能并不总是如此。

关键是你应该做一些事情,以防你有一个null 。 例如 – return或抛出IllegalStateException

而你不应该做的 – 不要捕获NullPointerException 。 运行时exception不是用于捕获 – 不期望您可以从它们中恢复,也不是依赖于逻辑流的exception的好习惯。 想象一下,你实际上并不期望某些东西是null ,并且你捕获(并记录)一个NullPointerException 。 这不是非常有用的信息,因为此时许多事情都可以为null

没有checkForNull方法可以编写,这将促进这一点(这不是方法调用和参数评估在Java中的工作方式)。

您可以将链式语句分解为多个语句,并在每个步骤进行检查。 但是,也许更好的解决方案是不要让这些方法首先返回null 。 你可能想要使用一种叫做Null Object Pattern的东西。

相关问题

  • 如何在Java中避免使用!= null语句?

很老的问题,但仍然添加我的建议:

我建议您不要从House获取DoorKnob,而应该尝试通过调用代码将DoorKnob提供给此类,或者通过创建专门用于此目的的中央查找工具(例如,DoorKnob服务)

你可以在java中支持这个(只是为了添加语法糖),使用一个技术模式,使用Mockito使用的技术:

 org.mockito.Mockito.spy(house).getFloor(0).getWall(WEST).getDoor().getDoorknob() 

或CatchException

 com.googlecode.catchexception.CatchException .catchException(house).getFloor(0).getWall(WEST).getDoor().getDoorknob() 

Mockito不能直接使用,因为检查非空房子并抛出exception,而CatchException是为不同目的而设计的。

然而,也许有人可以建议在哪里可以找到这样的function。