2013-10-28 43 views
7

在代码中我们有很多链方法,例如obj.getA().getB().getC().getD()。我想创建帮助类,它将检查方法getD()是否为空,但在此之前,我需要检查所有以前的获得者。我能做到这样:检查方法链中的最后一个getter是否为空

try { 
    obj.getA().getB().getC().getD(); 
} 
catch (NullPointerException e) { 
    // some getter is null 
} 

或(这是“傻”)

if (obj!null && obj.getA()!=null && obj.getA().getB()!=null && ...) { 
    obj.getA().getB().getC().getD(); 
} 
else { 
    // some getter is null 
} 

我不希望在我的代码使用try{} catch()每次去检查一下。什么是这个目的的最佳解决方案?

我认为最好的将是:

  1. obj.getA().getB().getC().getD().isNull() - 为此,我需要改变我所有的干将,例如执行包含isNull()方法的一些接口。
  2. NullObjectHelper.isNull(obj.getA().getB().getC().getD()); - 这将是最好的(我认为是这样),但如何实现呢?
+5

解决方案。重构。不要做链接#LawOfDemeter – user802421

+1

@ user802421我不想重构整个应用程序,这是很大的。我想要做的就是创建一些助手类。我同意你的观点,重构将是最好的,但我们没有时间。 – pepuch

+0

我没有看到你的两个“解决方案”如何帮助中间结果为'null' – Henry

回答

7

您可以通过Option模式实现预期结果。这会强制您更改方法签名,但基本上,如果您的方法返回某种类型T,它确保它具有一些非空值,并且如果它返回Option<T>,则表示它的值为T或null。

Java 7有一些称为无效安全的功能,但它从最终版本中删除。你可以这样做:

obj?.getA()?.getB()?.getC()?.getD() 

此外,爪哇8将添加一个名为Optional,所以你会做安全功能。

事实上,如果您现在真的想使用它,请尝试Null Object模式。这意味着,您可以返回某种默认值,而不是返回明码null,该值不会触发NullPointerException。虽然,你需要一些变化添加到您的吸气

class Object { 
    A getA() { 
    // ... 
    return a == null ? A.NULL : a; 
    } 
} 

class A { 
    static A NULL = new A(); // some default behaviour 
    B getB() { 
    if (this == NULL) return B.NULL; 
    // ... 
    return b == null ? B.NULL : b; 
    } 
} 

编辑:如果你想工具来做到这一点,你可以在一些功能界面包,然后调用它。

static boolean isNullResult(Callable call) throws Exception { 
    try { 
     return call.call() == null; 
    } catch (NullPointerException npe) { 
     return true; 
    } 
} 

用途将是如下:

isNullResult(new Callable<Integer>() { 
    @Override 
    public Integer call() throws Exception { 
     return new A().getB().getC().getInt(); 
    } 
}); 

它不会要求你改变现有的功能

+0

+1谢谢mishadoff的建议。我知道我可以使用'空对象'模式,但是如果我将使用它,我将需要对应用程序进行一些更改。我不想这样做,因为应用程序已经快完成了,而且我是这个项目的新手。 – pepuch

+0

@pepuch查看更新的实用程序使用解决方案 – mishadoff

1

如前所述,真正的解决办法是重构。

在此期间,你可以只换你的第一个解决方法中的一个功能:

static D getD(MyClass obj) { 

    try { 
     return obj.getA().getB().getC().getD(); 
    } 
    catch (NullPointerException e) { 
     return null; // Or even better, some default D 
    } 
} 

在主叫方网站:

D d = getD(obj); 

至少你不必垃圾来电与try-catch块。当某些中间getX()调用返回null,因此d变为null.最好的办法是在包装函数中返回一些默认的D


我怎么没看到你列出你的问题的最后两个选项倘使任何中间getX()的返回一个空;你会得到一个NullPointerException

+0

为了避免在上一个示例中使用NPE,我们可以将代码包装在Runnable模块中,稍后使用try-catch进行调用。 – mishadoff

+0

我不会推荐你使用过的实际代码。这听起来像A或B或C通常可能为空,因此在这种情况下投入异常通常是一个坏主意。当然,在实用方法中包装事物是一种很好的方法。然而,我非常喜欢这种实用方法,它包含了一些“无用的”代码,它可以进行空值检查而不是懒惰并捕获异常。 –

+0

@AndrzejDoyle **我首先会推荐重构。**潜在的空物体和链式吸气剂是缺陷的迹象。我对“愚蠢”代码的问题在于它很容易出错:很容易在那里犯一个错误,或者错过一个空检查等等。我们可以讨论try-catch与“愚蠢”的优缺点,片段,但解决方案是重构代码,以便我们不需要其中的任何一个......包装函数(无论是否使用try-catch或“愚蠢”片段都无关紧要)至少有助于保持新代码*相对*清洁;这是我的观点。 – Ali

2

从Java 8中,你可以使用像Optional.isPresentOptional.orElse方法吸链处理空:

boolean dNotNull = Optional.ofNullable(obj) 
       .map(Obj::getA) 
       .map(A::getB) 
       .map(B::getC) 
       .map(C::getD) 
       .isPresent(); 

虽然这是最好抓NullPointerException异常这种方法的缺点是对象分配可选实例。

有可能写,没有这方面的开销执行类似操作自己的静态方法:

boolean dNotNull = Nulls.isNotNull(obj, Obj::getA, A::getB, B::getC, C::getD); 

有关示例实现,请参阅Nullifier类型here

没有办法可能比嵌套检查if-not-null运行效率更高。

相关问题