2010-06-01 51 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); 
      } 
     } 
    }; 
} 

种类繁琐,可以通过使东西少通用避免。