Java 当首先检查结果是否为null时,如何避免冗余的方法调用?

Java 当首先检查结果是否为null时,如何避免冗余的方法调用?,java,Java,我发现在我的代码中,我多次调用相同的方法: private String doSomething(MyObject obj) { if (myObj == null || myObj.getA() == null || myObj.getA().getB() == null || myObjg.getA().getB().getC() == null) { throw new IllegalArgumentException("Erro

我发现在我的代码中,我多次调用相同的方法:

private String doSomething(MyObject obj) {
   if (myObj == null
      || myObj.getA() == null
      || myObj.getA().getB() == null
      || myObjg.getA().getB().getC() == null) {
         throw new IllegalArgumentException("Error message...");
   } else {
      return myObj.getA().getB().getC();
   }
}

如您所见,我多次调用getA、getB和getC。我希望避免这种情况,但仍然要检查null。避免这些冗余方法调用的好方法是什么?

从技术上讲,您不应该担心重复调用这些getter方法。getter应该是非常小的方法,因此当JIT在运行时显示值得对其进行优化时,它们将被内联到机器代码中,从而为您提供最大的性能。当JIT发现这不值得优化时,猜猜看:那么它就不值得优化了。当您的getter方法太大而无法内联时,您仍然会遇到一个真正的问题

但真正的答案是:首先不要违反法律

允许这样的变化是不好的做法。您展示的代码知道A有B有C。因此,此代码依赖于三个类A、B、C。如果不中断客户端,您的类A和B就无法更改。从这个角度来看,这种流畅的接口getA.getB.getC.doSomething应该被避免


这里真正的答案是将doSomething放在类A上,并隐藏所有潜在的依赖关系。

从技术上讲,您不应该担心重复调用这些getter方法。getter应该是非常小的方法,因此当JIT在运行时显示值得对其进行优化时,它们将被内联到机器代码中,从而为您提供最大的性能。当JIT发现这不值得优化时,猜猜看:那么它就不值得优化了。当您的getter方法太大而无法内联时,您仍然会遇到一个真正的问题

但真正的答案是:首先不要违反法律

允许这样的变化是不好的做法。您展示的代码知道A有B有C。因此,此代码依赖于三个类A、B、C。如果不中断客户端,您的类A和B就无法更改。从这个角度来看,这种流畅的接口getA.getB.getC.doSomething应该被避免


这里真正的答案是将doSomething放在类A上,并隐藏所有潜在的依赖关系。

这种模式不是问题所在。这是问题的征兆。真正的问题是,您已经编写了基于过程数据保持器的代码,该过程数据保持器包含一个接一个的可空字段链


因此,解决方案是编写代码。

这种模式不是问题所在。这是问题的征兆。真正的问题是,您已经编写了基于过程数据保持器的代码,该过程数据保持器包含一个接一个的可空字段链


因此,解决方案是编写代码。

我首选的模式替代方案是在每个访问阶段显式检查。这将导致更丰富、更详细的错误管理,而牺牲了一些额外的缩进

private String doSomething(MyObject myObj) {
    if(myObj != null) {
        A a = myObj.getA();
        if(a != null) {
            B b = a.getB();
            if(b != null) {
                return b.getC();
            } else {
                throw new IllegalArgumentException("myObj.a.b is null");
            }
        } else {
            throw new IllegalArgumentException("myObj.a is null");
        }
    } else {
        throw new IllegalArgumentException("myObj is null");
    }
}

请注意,if形状的这种深嵌套被认为是Arrow反模式的症状,它通常表示您应该进行一些重构。请记住,这只是该问题的一个指示器,而不是问题本身。

对于您的模式,我首选的替代方法是在每个访问阶段进行显式检查。这将导致更丰富、更详细的错误管理,而牺牲了一些额外的缩进

private String doSomething(MyObject myObj) {
    if(myObj != null) {
        A a = myObj.getA();
        if(a != null) {
            B b = a.getB();
            if(b != null) {
                return b.getC();
            } else {
                throw new IllegalArgumentException("myObj.a.b is null");
            }
        } else {
            throw new IllegalArgumentException("myObj.a is null");
        }
    } else {
        throw new IllegalArgumentException("myObj is null");
    }
}
请注意,if形状的这种深嵌套被认为是Arrow反模式的症状,它通常表示您应该进行一些重构。请记住,这只是该问题的一个指标,而不是问题本身。

,尤其是其方法,非常适合这种情况:

private String doSomething(MyObject obj) {
    return Optional.ofNullable(myObj)
        .map(MyObject::getA)
        .map(MyObject::getB)
        .map(MyObject::getC)
        .orElseThrow(() -> new IllegalArgumentException("Error message..."));
}
,尤其是其方法,非常适合这种情况:

private String doSomething(MyObject obj) {
    return Optional.ofNullable(myObj)
        .map(MyObject::getA)
        .map(MyObject::getB)
        .map(MyObject::getC)
        .orElseThrow(() -> new IllegalArgumentException("Error message..."));
}

将myObj.getA保存在局部变量中。但是getA.getB.getC无论如何都是一种代码味道,应该被删除/重构。那么私有字符串doSomethingMyObject myObj{try{return myObj.getA.getB.getC;}catch NullPointerException e{抛出新的IllegalArgumentExceptionError消息…;}@YCF_L呢,因为C==null最初会导致异常,而在您的示例中不会。请检查我的问题,并检查它真正有帮助的答案。将myObj.getA保存在局部变量中。但是getA.getB.getC无论如何都是一种代码味道,应该被删除/重构。那么私有字符串doSomethingMyObject myObj{try{return myObj.getA.getB.getC;}catch NullPointerException e{抛出新的IllegalArgumentExceptionError消息…;}@YCF_L呢,因为C==null最初会导致异常,而在您的示例中不会。请检查我的问题,并检查它真正有帮助的答案
. 我一般同意你提到的LoD,但是这些类的结构必须是这样的,因为它们代表我通过JSON接收的DTO。@FischerLudrian我更新了我的答案,也谈到了优化部分。希望这会有所帮助。@FischerLudrian除此之外,它可能是一个增强DTO类的选项,至少可以隐藏此链接。是什么阻止您在类a上简单地添加doSomething方法?我大体上同意您提到的LoD,但这些类的结构必须是这样的,因为它们代表我通过JSON接收的DTO。@FischerLudrian我更新了我的答案,也谈到了优化部分。希望这会有所帮助。@FischerLudrian除此之外,它可能是一个增强DTO类的选项,至少可以隐藏此链接。是什么阻止您在类a上简单地添加doSomething方法?是的,但是如果我通过REST接口以这种形式获取这些数据会怎么样?@Fischer您将收到的JSON数据映射到真实对象而不是简单的DTO上,然后同样的建议再次适用。是的,但是,如果我通过REST接口以这种形式获取这些数据呢?@Fischer您将接收到的JSON数据映射到真实对象而不是简单的DTO,然后同样的建议再次适用。