检查“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());
此代码也是类型安全的,通常按预期工作:
- 如果链中的所有对象都不为null ,则返回指定类型的实际值。
- 如果链中的任何对象为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。