2013-07-09 31 views
2

我是一個Objective-C人員,開始一個小型個人Java項目。在我的Objective-C的項目,我練防守編碼和經常使用Wil Shipley's IsEmpty(id thing) method使用前要檢查空或空值:Wil Shipley用於防禦性編碼的`IsEmpty`方法的Java實現

static inline BOOL IsEmpty (id thing) 
{ 
    return thing == nil 
     || ([thing respondsToSelector:@selector(length)] && [(NSData *)thing length] == 0) 
     || ([thing respondsToSelector:@selector(count)] && [(NSArray *)thing count] == 0); 
} 

我知道,Apache的百科全書的做這樣的事情(儘管它看起來我只用於String對象),並且我嘗試在Java中實現類似的東西,但是在編譯時類未知時,找不到方法在對象上調用length()size()方法。

我是不是想把一個方形的釘子插入圓孔?如果不是,那麼經驗豐富的Java開發人員如何實施通用檢查以確保大範圍的對象不爲空,並且如果它們實現了.length().size()方法則不是空的?

編輯:雖然一些答案側重於執行等效isEmpty方法(和pgreen2的回答似乎工作)由CodaFi突出了實際問題的Tosters似乎是Objective-C的編碼做法利用弱類型可能無法很好地轉化爲像Java這樣的強類型範例(「square peg,round hole」)。這並不是說任何一種打字方法都是可取的;他們只是不同,需要不同的做法。

+0

你可能會。 Objective-C繼承了C的弱於弱的類型系統,然後在它上面打了一層鴨子。 Java的比較嚴格得多。 – CodaFi

回答

2

我沒有經驗,但它看起來像鍵入比java更弱。你的問題的第一部分非常簡單。

//this checks for null objects 
public static boolean isEmpty(Object o) { 
    return o == null; 
} 

但是,在您的示例代碼中,您正在檢查具有特定名稱的特定字段。這在java中要複雜得多。您可以使用reflection,但您需要檢查兩種方法和字段。然後您將需要檢查適當的返回類型:Number,int,long,short,float,double。所以,這是可能的,但是需要一些工作,而且會很慢。

甲簡單,但更嚴格的機制將只檢查常見類型: (更新爲修正錯誤用枚舉和添加陣列;陣列具有被分解爲每個靈長類動物由於陣列中的java是如何工作的)

public static boolean isEmpty(Object o) { 
    if (o == null) { 
     return true; 
    } 
    else if (o instanceof Object[]) { 
     return ((Object[]) o).length <= 0; 
    } 
    else if (o instanceof boolean[]) { 
     return ((boolean[]) o).length <= 0; 
    } 
    else if (o instanceof byte[]) { 
     return ((byte[]) o).length <= 0; 
    } 
    else if (o instanceof short[]) { 
     return ((short[]) o).length <= 0; 
    } 
    else if (o instanceof char[]) { 
     return ((char[]) o).length <= 0; 
    } 
    else if (o instanceof int[]) { 
     return ((int[]) o).length <= 0; 
    } 
    else if (o instanceof long[]) { 
     return ((long[]) o).length <= 0; 
    } 
    else if (o instanceof float[]) { 
     return ((float[]) o).length <= 0; 
    } 
    else if (o instanceof double[]) { 
     return ((double[]) o).length <= 0; 
    } 
    else if (o instanceof CharSequence) { 
     return ((CharSequence) o).length() <= 0; 
    } 
    else if (o instanceof Collection) { 
     return ((Collection) o).isEmpty(); 
    } 
    else if (o instanceof Map) { 
     return ((Map) o).isEmpty(); 
    } 
    else if (o instanceof Enumeration) { 
     return !((Enumeration) o).hasMoreElements(); 
    } 
    else if (o instanceof Dictionary) { 
     return ((Dictionary) o).isEmpty(); 
    } 
    else if (o instanceof Iterable) { 
     // NOTE: may not be efficient because an iterator is created 
     return !((Iterable) o).iterator().hasNext(); 
    } 

    return false; 
} 

更新:下面是以前的版本與反思檢查方法,代碼可以擴展到支持領域。處理退貨類型並不像我最初想象的那麼困難。即使有反思,它也會貼上自動裝箱作品。我也扔了一個檢查,看看對象是否有一個isEmpty()布爾方法。

public static boolean isEmpty(final Object o) { 
    if (o == null) { 
     return true; 
    } 
    else if (o instanceof Object[]) { 
     return ((Object[]) o).length <= 0; 
    } 
    else if (o instanceof boolean[]) { 
     return ((boolean[]) o).length <= 0; 
    } 
    else if (o instanceof byte[]) { 
     return ((byte[]) o).length <= 0; 
    } 
    else if (o instanceof short[]) { 
     return ((short[]) o).length <= 0; 
    } 
    else if (o instanceof char[]) { 
     return ((char[]) o).length <= 0; 
    } 
    else if (o instanceof int[]) { 
     return ((int[]) o).length <= 0; 
    } 
    else if (o instanceof long[]) { 
     return ((long[]) o).length <= 0; 
    } 
    else if (o instanceof float[]) { 
     return ((float[]) o).length <= 0; 
    } 
    else if (o instanceof double[]) { 
     return ((double[]) o).length <= 0; 
    } 
    else if (o instanceof CharSequence) { 
     return ((CharSequence) o).length() <= 0; 
    } 
    else if (o instanceof Collection) { 
     return ((Collection) o).isEmpty(); 
    } 
    else if (o instanceof Map) { 
     return ((Map) o).isEmpty(); 
    } 
    else if (o instanceof Enumeration) { 
     return !((Enumeration) o).hasMoreElements(); 
    } 
    else if (o instanceof Dictionary) { 
     return ((Dictionary) o).isEmpty(); 
    } 
    else if (o instanceof Iterable) { 
     // NOTE: may not be efficient because an iterator is created 
     return !((Iterable) o).iterator().hasNext(); 
    } 

    // reflection code 

    final Number length = retrieveNumberFromMethod(o, "length"); 
    if (length != null) { 
     return length.shortValue() <= 0; 
    } 

    final Number size = retrieveNumberFromMethod(o, "size"); 
    if (size != null) { 
     return size.shortValue() <= 0; 
    } 

    final Boolean isEmpty = retrieveBooleanFromMethod(o, "isEmpty"); 
    if (isEmpty != null) { 
     return isEmpty; 
    } 

    return false; 
} 

static Number retrieveNumberFromMethod(final Object o, final String methodName) { 
    try { 
     final Number number = (Number) o.getClass().getMethod(methodName).invoke(o); 
     return number; 
    } 
    catch (final IllegalArgumentException e) { 
     throw new IllegalStateException("Unable to retrieve number from " + methodName + " on " + o, e); 
    } 
    catch (final SecurityException e) { 
     throw new IllegalStateException("Unable to retrieve number from " + methodName + " on " + o, e); 
    } 
    catch (final InvocationTargetException e) { 
     throw new IllegalStateException("Unable to retrieve number from " + methodName + " on " + o, e); 
    } 
    catch (final IllegalAccessException e) { 
     return null; 
    } 
    catch (final NoSuchMethodException e) { 
     return null; 
    } 
} 

static Boolean retrieveBooleanFromMethod(final Object o, final String methodName) { 
    try { 
     final Boolean bool = (Boolean) o.getClass().getMethod(methodName).invoke(o); 
     return bool; 
    } 
    catch (final IllegalArgumentException e) { 
     throw new IllegalStateException("Unable to retrieve boolean from " + methodName + " on " + o, e); 
    } 
    catch (final SecurityException e) { 
     throw new IllegalStateException("Unable to retrieve boolean from " + methodName + " on " + o, e); 
    } 
    catch (final InvocationTargetException e) { 
     throw new IllegalStateException("Unable to retrieve boolean from " + methodName + " on " + o, e); 
    } 
    catch (final IllegalAccessException e) { 
     return null; 
    } 
    catch (final NoSuchMethodException e) { 
     return null; 
    } 
} 
1

退房類Class和Java類Method,害得我下面的實施isEmpty

public static boolean isEmpty(Object o) { 
    try { 
     return o == null || 
      (0 == (int)(((Class<? extends Object>) (o.getClass())) 
       .getMethod("size", (Class[]) null).invoke(o, (Object[]) null))); 
    } catch (IllegalAccessException | IllegalArgumentException 
     | InvocationTargetException | SecurityException e) { 
     e.printStackTrace(); 
     return true; 
    } catch (NoSuchMethodException e) { 
     return true; 
    } 
} 

對於那些需要參數的方法,在getMethod()invoke()作爲替代null小號根據MethodClass的文檔。

附錄

我知道size()返回一個int,所以我毫無顧忌鑄造invoke()結果。這個修訂的isEmpty()實現更清潔和更明確的關於它在做什麼:

public static boolean isEmpty(Object o) { 
    if (o != null) { 
     Class<? extends Object> c = o.getClass(); 
     try { 
     Method m = c.getMethod("size", (Class[]) null); 
     Integer result = (Integer) m.invoke(o, (Object[]) null); 
     if (result.intValue() > 0) { 
      return false; 
     } 
     } catch (NoSuchMethodException e) { 
     // o doesn't have a "size", so we'll quietly move on to return true 
     // indicating this object is non-accessible 
     } catch (IllegalAccessException | IllegalArgumentException 
      | InvocationTargetException | SecurityException e) { 
     // got something unexpected, let's show how we got here and then 
     // return true, indicating this object is non-accessible 
     e.printStackTrace(); 
     } 
    } 
    return true; 
} 
+0

不錯,但是當我把所有上面的代碼粘貼到帶有javac 1.6和1.7的IntelliJ IDEA 12中時,isEmpty方法顯示了一個'Inconvertible types;無法將'java.lang.Object'強制轉換爲int''錯誤和紅色語法突出顯示。它爲你編譯?哪個JDK和IDE? –

+1

@SteveHHH在OS X Mountain Lion的Java 1.7中編譯和運行良好,我在Eclipse中編寫它。你可以把它分解成幾個階段,創建中間的Class和Method對象來查看特定IntelliJ窒息的地方。 – pjs

3

在我看來這是Java非常不好的習慣。由於許多instanceof檢查或try/catch遞歸,此方法將「非常繁重」。另外上面的一些例子會導致你丟失信息,如果收集是NULL或只是空的。 在OBJ-C中,這是沒有問題的,因爲你總是可以調用nil指針的方法,但是這對於java來說是非常不同的。我相信這可能會導致許多問題適當的算法控制。例如,你可以這樣做的條件一樣

if (Helper.isEmpty(someList) == true) { 
    someList = new ArrayList<>(); 
} 

,如果這是由幾個方法調用作爲參數的緩衝區傳下來的陣列,這可能會導致錯誤,這將是很難找到。

基本上進入你的方法你知道什麼類型的對象出現,所以你可以做即時檢查,而不用經過長時間的類型檢查方法,其中幾個if後發現參數類型從一開始就有哪些知識。

+1

很好的答案。儘管pgreen2的答案有效,但真正的問題似乎是Objective-C的弱類型使其與Java有着根本的不同,並且有些練習不能很好地從Objective-C轉換爲Java(「方形釘,圓孔」)。 –