简体   繁体   English

如何更好地重构可以在 java 中返回 null 的方法链?

[英]How better refactor chain of methods that can return null in java?

I have code like:我有这样的代码:

obj1 = SomeObject.method1();
if (obj1 != null) {
  obj2 = obj1.method2();
  if (obj2 != null) {
     obj3 = obj2.method3();
     if (obj3 != null) {
              ............


     return objN.methodM();

   }
  }
 }
....

I have near 10 steps.我有近10个步骤。 It seems very fragile and error prone.它似乎非常脆弱且容易出错。 Is there a better way to check on null chained methods?有没有更好的方法来检查空链方法?

Thanks.谢谢。

More context is necessary to answer this question well.需要更多的上下文才能很好地回答这个问题。

For example, in some cases I'd advocate breaking out the inner if statements into their own methods, following the "each method should do a single thing, completely and correctly."例如,在某些情况下,我主张将内部if语句分解为它们自己的方法,遵循“每个方法都应该完全正确地做一件事情”。 In this case, calling the method and checking for null is that single thing: if it's null, it returns (or throws, depending on your actual needs).在这种情况下,调用该方法并检查 null一件事:如果它为 null,则返回(或抛出,取决于您的实际需要)。 If it isn't, it calls the next method.如果不是,则调用下一个方法。

Ultimately I suspect this is a design issue, though, the solution to which is unknowable without insight into the problem being solved.最终我怀疑这是一个设计问题,但是,如果不深入了解正在解决的问题,解决方案是不可知的。

As it stands, this single chunk of code requires deep knowledge of (what I suspect are) multiple responsibilities, meaning in almost all cases, new classes, new patterns, new interfaces, or some combination would be required to make this both clean, and understandable.就目前而言,这一段代码需要深入了解(我怀疑是什么)多个职责,这意味着在几乎所有情况下,都需要新类、新模式、新接口或某种组合才能使其既干净又可以理解。

We can use Java8 Functional Interface approach.我们可以使用 Java8 函数式接口方​​法。

@FunctionalInterface
public interface ObjectValue<V> {
    V get();
}

static <V> V getObjectValue(ObjectValue<V> objectValue)  {
    try {
        return objectValue.get();
    } catch (NullPointerException npe) {
        return null;
    }
}

Object obj = getObjectValue(() -> objectA.getObjectB().getObjectC().getObjectD());
if(Objects.nonNull(obj)) {
//do the operation
}

Write like像这样写

obj1 = SomeObject.method1();
if (obj1 == null) 
    return;
 obj2 = obj1.method2();
 if (obj2 == null) 
    return;

etc. As a C developer this is a very common paradigm and is extremely common.等等。作为 C 开发人员,这是一个非常常见的范例,并且非常普遍。 If it's not possible to convert your code to this flat flow then your code needs to be refactored in the first place, regardless of what language it exists in.如果无法将您的代码转换为这种平面流,那么您的代码首先需要重构,无论它使用哪种语言。

Replace return with whatever you are actually doing in the case where these fail, whether that be return null , throw an exception, etc. - you've omitted that part of your code but it should be the same logic.在这些失败的情况下,将return替换为您实际正在做的任何事情,无论是return null throw异常等 - 您已经省略了代码的那部分,但它应该是相同的逻辑。

You can use java.util.Optional.map(..) to chain these checks:您可以使用java.util.Optional.map(..)来链接这些检查:

return Optional.ofNullable(SomeObject.method1())
        .map(SomeObject2::method2)
        .map(SomeObject3::method3)
        // ....
        .map(SomeObjectM::methodM)
        .orElse(null);

It's common problem for null references in java. java中的null引用是一个常见的问题。

I prefer chaining with && :我更喜欢用&&链接:

if (obj1 != null && obj1.method1() != null && obj1.method1().method2() != null)

I thinh this kind of question was already answered here .我认为这种问题已经在这里得到了回答。 Especially see the second aswer about Null Object Pattern .尤其是关于Null Object Pattern的第二个答案。

You can chain them and surround everything with a try/catch and catch the NPE.您可以链接它们并使用 try/catch 包围所有内容并捕获 NPE。

Like this:像这样:

try
{
    Object result = SomeObject.method1().method2().methodN();
    return result;
}
catch(NullPointerException ex)
{
     // Do the errorhandling here.
}

Outside that I second @Neil's comment: Try to avoid that kind of chains in the first place.除此之外,我第二个@Neil 的评论:首先尽量避免这种链条。

EDIT:编辑:

Votes show that this is very disputed.投票表明,这是非常有争议的。 I want to make sure it is understood, that I do not actually recommend this!我想确保它被理解,我实际上并不推荐这个!

Proceeding like this has many sideeffects and should be generally avoided.像这样进行有很多副作用,一般应该避免。 I just threw it into discussion for OPs special situation, only as one way to achieve the goal if not otherwise possible!我只是将其用于 OP 特殊情况的讨论中如果没有其他可能,仅作为实现目标的一种方式!

If anyone feels he needs to do this: Please read the comments for possible pitfalls!如果有人觉得他需要这样做:请阅读评论以了解可能的陷阱!

Try to format this way:尝试以这种方式格式化:

obj1 = SomeObject.method1();
if (obj1 != null) {
   obj2 = obj1.method2();
}
if (obj2 != null) {
    obj3 = obj2.method3();
}
if (obj3 != null) {
          ............
}

if (objN != null) {
   return objN.methodM();
}
return null;

Don't forget to initialize all your obj s to null .不要忘记将所有obj初始化为null

obj1 = SomeObject.method1();
if (obj1 == null) throw new IllegalArgumentException("...");

obj2 = obj1.method2();
if (obj2 == null) throw new IllegalArgumentException("...");

obj3 = obj2.method3();
if (obj3 == null) throw new IllegalArgumentException("...");

if (objN != null) {
   return objN.methodM();
}

Some more discussion here 这里还有一些讨论

If you are on Java 8 or higher, consider Optional :如果您使用的是 Java 8 或更高版本,请考虑Optional

If you want are so trusting of your source objects that you plan to chain six of them together, then just continue to trust them and catch exceptions when they are thrown - hopefully rarely.如果您希望如此信任您的源对象,以至于您计划将其中的六个链接在一起,那么只需继续信任它们并在抛出异常时捕获异常 - 希望很少。

However if you decide not to trust your source objects, then you have two choices: add compulsive "!= null" checks everywhere and don't chain their methods...但是,如果您决定不信任您的源对象,那么您有两个选择:在任何地方添加强制“!= null”检查并且不要链接它们的方法......

OR go back and change your source object classes and add better null handling at the root.或者返回并更改您的源对象类并在根添加更好的空处理。 You can do it by hand (with null checks inside setters, for example), or alternatively you can use the Optional class in Java 8 (or Optional in Google's Guava if you're not on Java 8), which offers an opinionated null-handling design pattern to help you react to unwanted nulls the moment they are introduced, rather than wait for some poor consumer to encounter them later on.您可以手动完成(例如,在 setter 中进行空检查),或者您可以使用Java 8 中Optional类(如果您不是在 Java 8 上,则可以使用Google 的 Guava 中的 Optional ),它提供了一个固执的 null-处理设计模式以帮助您在引入不需要的空值时做出反应,而不是等待一些可怜的消费者稍后再遇到它们。

For getter method who has no parameter , try this:对于没有参数的getter方法,试试这个:

Util.isNull(person, "getDetails().iterator().next().getName().getFullName()")

for most of the cases, it works well.在大多数情况下,它运行良好。 Basically, it is trying to use java reflection to do the null check layer by layer, till it reach the last getter method , since we do a lot cacheing for the reflection, the code works well in production.基本上,它是尝试使用java反射逐层进行空检查,直到到达最后一个getter方法,因为我们为反射做了很多缓存,代码在生产中运行良好。 Please check the code below.请检查下面的代码。

public static boolean isNull(Object obj, String methods) {
    if (Util.isNull(obj)) {
        return true;
    }
    if (methods == null || methods.isEmpty()) {
        return false;
    }
    String[] A = methods.split("\\.");
    List<String> list = new ArrayList<String>();
    for (String str : A) {
        list.add(str.substring(0, str.indexOf("(")).trim());
    }
    return isNullReflect(obj, list);
}
public static boolean isNullReflect(Object obj, List<String> methods) {
    if (Util.isNull(obj)) {
        return true;
    }
    if (methods.size() == 0) {
        return obj == null;
    }
    Class<?> className = Util.getClass(obj);
    try {
        Method method = Util.getMethod(className.getName(), methods.remove(0), null, className);
        method.setAccessible(true);
        if (method.getName().equals("next")
                && !Util.isNull(Util.getMethod(className.getName(), "hasNext", null, className))) {
            if (!((Iterator<?>) (obj)).hasNext()) {
                return true;
            }
        }
        try {
            return isNullReflect(method.invoke(obj), methods);
        } catch (IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    } catch (SecurityException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    return false;
}


public static Boolean isNull(Object object) {
    return null == object;
}

public static Method getMethod(String className, String methodName, Class<?>[] classArray, Class<?> classObj) {
    // long a = System.nanoTime();
    StringBuilder sb = new StringBuilder();
    sb.append(className);
    sb.append(methodName);
    if (classArray != null) {
        for (Class<?> name : classArray) {
            sb.append(name.getName());
        }
    }
    String methodKey = sb.toString();
    Method result = null;
    if (methodMap.containsKey(methodKey)) {
        return methodMap.get(methodKey);
    } else {
        try {
            if (classArray != null && classArray.length > 0) {
                result = classObj.getMethod(methodName, classArray);
            } else {
                result = classObj.getMethod(methodName);
            }
            methodMap.put(methodKey, result);
        } catch (NoSuchMethodException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (SecurityException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    // long b = System.nanoTime();
    // counter += (b - a);
    return result;
}
    private static Map<String, Method> methodMap = new ConcurrentHashMap<String, Method>();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM