2010-06-01 43 views
1

有沒有辦法在迭代器中處理並繼續 - 異常,同時保持foreach語法糖?Iterable中的異常處理

我有一個解析器遍歷一個文件中的行,並返回一個每行的類。偶爾線條在語法上將是虛構的,但這並不意味着我們不應該繼續閱讀文件。

我的解析器實現可迭代,但對付潛在的異常意味着寫

for (Iterator iter = myParser.iterator(); iter.hasNext();) { 
    try { 
    MyClass myClass = iter.next(); 
    // .. do stuff .. 
    } catch (Exception e) { 
    // .. do exception stuff .. 
    } 
} 

..沒有錯,但有越來越異常處理上隱含單個iter.next的任何方式()調用在foreach構造?

回答

2

你當然可以換你的迭代器在另一迭代器,像這樣:

public class SafeIterator<E> implements Iterator<E>{ 
    private Iterator<E> inner; 
    public SafeIterator(Iterator<E> inner){this.inner=inner;} 
    public boolean hasNext(){return inner.hasNext();} 
    public E next(){ 
     try{ 
      return inner.next(); 
     }catch(Exception e){return null;} //you'll also want to do some logging here 
    } 
} 

編輯:

這裏的問題是,a)您是打破了迭代器合同。 hasNext()表明,一個值來了接下來將在這個版本中可能會或可能不會提供一個值(它可能爲空),所以客戶端總是做null檢查:

for(MyType m : myIterator){ 
    if(m!=null){ 
     // do stuff here 
    } 
} 

這當然需要最遠離迭代器模式的優雅,但更重要的是b)你正在拋出異常。我們違反Josh Bloch(Effective Java)項目#65:「不要忽視異常」。我們通過忽略不良的價值觀來解決錯誤的問題。解析器應該學會處理不好的值(也許通過跳到下一個好的值),或者不應該有任何不好的值。

+0

顯示這種方法的問題(你必須處理循環體中的空值),並得到+1票。 – 2010-06-01 05:04:14

+0

這種方法存在多個問題,但我會立即闡述。 – 2010-06-01 07:41:41

4

就個人而言,我會做到這一點使用guava的(原谷歌集合)AbstractIterator - 看到的Javadoc該類一個簡單的空跳躍例如,它可以很容易地修改:

public static Iterator<MyClass> skipExceptions(final Iterator<MyClass> in) { 
    return new AbstractIterator<MyClass>() { 
    protected String computeNext() { 
     while (in.hasNext()) { 
     try { 
      return in.next(); 
     } catch (MalformedThingyException e) { 
      // Do nothing, skip to the next one 
     } 
     } 
     return endOfData(); 
    } 
    }; 
}} 

for (MyClass thing : skipExceptions(myParser.iterator()) { 
    // Do something ONLY to those items that didn't cause an exception 
} 

是的,這是稍微詳細,但它也是通用的和可重複使用的容易(也很乾淨主叫端):

然後使用這個時候,因爲它是那麼簡單。

+0

這種方法是我想說的下一個,但我不知道解決方案已經存在。 – 2010-06-01 07:51:49

3

我很困惑。我看到了一個簡單的迭代器模式,典型的iter.hasNext()和iter.next()調用並問自己爲什麼需要對這些迭代器操作進行特殊的異常處理。迭代器有一個下一個元素並從集合中返回它,否則它會被hasNext()調用檢測到。

所以我開始質疑一切,然後它回到我的腦海,你基本上使用迭代器模式以.. ..非傳統的方式。您不是遍歷集合或通過文件的行迭代,而是使用Iterator#next()將基於文本的模型解析爲對象。

下面是從維基百科短定義:

在面向對象的編程中, Iterator模式是其中迭代器被用來訪問聚合對象 依次一個的 元件不暴露設計圖案 其底層代表 。

對我來說,使用文本文件作爲線條集合和迭代是可以接受的。但是,迭代器應該返回文本行(字符串)。你真的應該在代碼中分開迭代和解析,如下面的代碼片段所示。要麼你有一個解析文本文件,並提供一個iterator爲MyClass的對象分析器或您遍歷文本文件,並解析線一個接一個:

public void snippet1(String[] lines, MyParser, myParser) { 
    for (String line:lines) { 
    try { 
     MyClass myClass = myParser.parse(line); 
    } catch (Exception e) { 
     // handle/report unparsable lines 
    } 
    } 
} 

public void snippet2(String[] lines) { 
    try { 
     MyParser myParser = new MyParser(); 
     myParser.parse(lines); 
    } catch (Exception e) { 
     // handle/report unparsable lines 
    } 
    for (MyClass myClass:myParser) { 
     // do something with myClass object 
    } 
} 
+0

是。我得到了最多的選票,但實際上我喜歡其他答案比我自己更多:-) – 2010-06-01 07:53:06

1

有沒有處理任何方式 - 並繼續 - 在保持foreach語法糖的同時迭代器中的異常?

沒有這樣的糖。

偶爾行將語法上僞造,但這並不意味着我們不應該繼續閱讀文件。

那麼,如果它不是特殊的行是假的,爲什麼會拋出異常?你可以重寫你的迭代器。假設您目前迭代ParsedThingy實例和解析器拋出ThingyParseException如果解析失敗,遍歷包裝這讓您查詢bogusness的解析結果,就像這樣:

for (Possibly<ParsedThingy, ThingyParseException> p : parser) { 
    if (p.exception() != null) handleException(p.exception()); 
    else doSomethingExcitingWith(p.value()); 
} 

某種程度上更自我記錄比看似自發地返回null s;它還可以讓您向客戶端代碼提供有關錯誤的信息。

Possibly<V, X>是一個可能實際上是一個例外的值的包裝。您可以通過檢查exception()非空查詢一個特殊的地位,並通過調用value()得到的非特殊情況下的值(這將拋出,如果它是一個例外):

class Possibly<V, X extends Throwable> { 
    private final V value; 
    private final X exception; 
    public static <V, X extends Throwable> Possibly<V, X> forValue(V v){ 
     return new Possibly<V, X>(v, null); 
    } 
    public static <V, X extends Throwable> Possibly<V, X> forException(X x){ 
     if (x == null) throw new NullPointerException(); 
     return new Possibly<V, X>(null, x); 
    } 
    private Possibly(V v, X x){ value = v; exception = x; } 
    public X exception(){ return exception; } 
    public V value() throws X { 
     if (exception != null) throw exception; 
     return value; 
    } 
} 

然後你iterator()將看起來像這樣:

Iterator<Possibly<ParsedThingy, ThingyParseException>> parse() { 
    return new Iterator<Possibly<ParsedThingy, ThingyParseException>> { 
     public boolean hasNext(){ ... } 
     public void remove(){ ... } 
     public Possibly<ParsedThingy, ThingyParseException> next() 
      try { 
       ParsedThingy t = parseNext(); // throws ThingyParseException 
       return Possibly.forValue(t); 
      } catch (ThingyParseException e) { 
       return Possibly.forException(e); 
      } 
     } 
    }; 
} 

種類繁瑣,可以通過使東西少通用避免。