2010-06-01 3 views
1

Есть ли способ обработки и продолжения исключения из итератора при сохранении синтаксического сахара foreach?Обработка исключений в Iterable

У меня есть синтаксический анализатор, который выполняет итерации по строкам в файле, передавая класс за строку. Иногда строки будут синтаксически фиктивными, но это не обязательно означает, что мы не должны продолжать читать файл.

Мой анализатор реализует 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 
    } 
} 

EDIT:

беда в том, что а) вы нарушаете договор итератора. hasNext() предполагает, что значение будет дальше, пока эта версия может или не может обеспечить значение (это может быть нулевым), поэтому клиент всегда должен делать нулевые чеки:

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

, который, конечно, занимает большую часть элегантность от шаблона итератора, но, что более важно, б) вы исключаете исключения. Мы нарушаем статью Джоша Блоха (Эффективная Java) № 65: «Не игнорируйте исключения». Мы решаем неправильную проблему, игнорируя плохие значения. Парсер должен либо научиться иметь дело с плохими значениями (возможно, пропустив вперед до следующего хорошего значения), либо не должно быть никаких плохих значений.

+0

Покажите проблему с этим подходом (вам нужно иметь дело с нулями в теле цикла), и вы получите +1 голос. –

+0

есть несколько проблем с этим подходом, но я сразу остановлюсь. –

4

Лично я хотел бы сделать это с помощью guava «S (ранее Google-коллекция) 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

Этот подход был следующим, который я бы предложил, но я не знал, что решение уже существует. –

3

Я был довольно смущен. Я видел простой шаблон итератора, типичные вызовы iter.hasNext() и iter.next() и спрашивал себя, зачем нужна специальная обработка исключений для этих операций итератора. Итератор либо имеет следующий элемент и возвращает его из коллекции, либо нет, и это обнаружено вызовом hasNext().

Итак, я начал все спрашивать, а потом мне пришло в голову, что вы в основном используете шаблон итератора в .. говорите .. нетрадиционным способом. Вы не выполняете итерацию через коллекцию или через строки файла, но используете Iterator # next() для анализа текстовой модели в объекты.

Вот короткое определение из википедии:

В объектно-ориентированном программировании итератор картины шаблон проектирования , в которых итераторы используются для доступа элементов совокупного объекта последовательно, не подвергая его базовое представление.

Для меня приемлемым является использование текстового файла в виде набора строк и итерации. Но тогда итератор должен возвращать текстовые строки (строки). Вам действительно следует отделить итерацию и синтаксический анализ вашего кода, как показано в следующих фрагментах. Либо у вас есть анализатор, который анализирует текстовый файл и предоставляет итератор для объектов 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

yup. У меня больше голосов, но на самом деле мне нравятся и другие ответы больше, чем мои собственные :-) –

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); 
      } 
     } 
    }; 
} 

Вид многословного, которого можно было бы избежать, сделав материал менее общим.

Смежные вопросы