簡體   English   中英

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

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

我有這樣的代碼:

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


     return objN.methodM();

   }
  }
 }
....

我有近10個步驟。 它似乎非常脆弱且容易出錯。 有沒有更好的方法來檢查空鏈方法?

謝謝。

需要更多的上下文才能很好地回答這個問題。

例如,在某些情況下,我主張將內部if語句分解為它們自己的方法,遵循“每個方法都應該完全正確地做一件事情”。 在這種情況下,調用該方法並檢查 null一件事:如果它為 null,則返回(或拋出,取決於您的實際需要)。 如果不是,則調用下一個方法。

最終我懷疑這是一個設計問題,但是,如果不深入了解正在解決的問題,解決方案是不可知的。

就目前而言,這一段代碼需要深入了解(我懷疑是什么)多個職責,這意味着在幾乎所有情況下,都需要新類、新模式、新接口或某種組合才能使其既干凈又可以理解。

我們可以使用 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
}

像這樣寫

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

等等。作為 C 開發人員,這是一個非常常見的范例,並且非常普遍。 如果無法將您的代碼轉換為這種平面流,那么您的代碼首先需要重構,無論它使用哪種語言。

在這些失敗的情況下,將return替換為您實際正在做的任何事情,無論是return null throw異常等 - 您已經省略了代碼的那部分,但它應該是相同的邏輯。

您可以使用java.util.Optional.map(..)來鏈接這些檢查:

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

java中的null引用是一個常見的問題。

我更喜歡用&&鏈接:

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

我認為這種問題已經在這里得到了回答。 尤其是關於Null Object Pattern的第二個答案。

您可以鏈接它們並使用 try/catch 包圍所有內容並捕獲 NPE。

像這樣:

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

除此之外,我第二個@Neil 的評論:首先盡量避免這種鏈條。

編輯:

投票表明,這是非常有爭議的。 我想確保它被理解,我實際上並不推薦這個!

像這樣進行有很多副作用,一般應該避免。 我只是將其用於 OP 特殊情況的討論中如果沒有其他可能,僅作為實現目標的一種方式!

如果有人覺得他需要這樣做:請閱讀評論以了解可能的陷阱!

嘗試以這種方式格式化:

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;

不要忘記將所有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();
}

這里還有一些討論

如果您使用的是 Java 8 或更高版本,請考慮Optional

如果您希望如此信任您的源對象,以至於您計划將其中的六個鏈接在一起,那么只需繼續信任它們並在拋出異常時捕獲異常 - 希望很少。

但是,如果您決定不信任您的源對象,那么您有兩個選擇:在任何地方添加強制“!= null”檢查並且不要鏈接它們的方法......

或者返回並更改您的源對象類並在根添加更好的空處理。 您可以手動完成(例如,在 setter 中進行空檢查),或者您可以使用Java 8 中Optional類(如果您不是在 Java 8 上,則可以使用Google 的 Guava 中的 Optional ),它提供了一個固執的 null-處理設計模式以幫助您在引入不需要的空值時做出反應,而不是等待一些可憐的消費者稍后再遇到它們。

對於沒有參數的getter方法,試試這個:

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

在大多數情況下,它運行良好。 基本上,它是嘗試使用java反射逐層進行空檢查,直到到達最后一個getter方法,因為我們為反射做了很多緩存,代碼在生產中運行良好。 請檢查下面的代碼。

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