Что делать с исключениями при реализации java.lang.Iterator - PullRequest
29 голосов
/ 05 февраля 2010

Интерфейс java.lang.Iterator имеет 3 метода: hasNext, next и remove. Чтобы реализовать итератор только для чтения, вы должны предоставить реализацию для двух из них: hasNext и next.

Моя проблема в том, что эти методы не объявляют никаких исключений. Поэтому, если мой код внутри итерационного процесса объявляет исключения, я должен заключить свой итерационный код в блок try / catch.

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

Как обойти это ограничение в классе Iterator?

Вот пример кода для наглядности:

class MyIterator implements Iterator
{
    @Override
    public boolean hasNext()
    {
        try
        {
            return implementation.testForNext();
        }
        catch ( SomethingBadException e ) 
        {
            throw new RuntimeException(e);
        }
    }

    @Override
    public boolean next()
    {
        try
        {
            return implementation.getNext();
        }

        catch ( SomethingBadException e ) 
        {
            throw new RuntimeException(e);
        }
    }

    ...
}

Ответы [ 10 ]

11 голосов
/ 05 февраля 2010

Вы должны перебросить исключение как пользовательское исключение времени выполнения, а не универсальное, например SomethingBadRuntimeException. Также вы можете попробовать туннелирование исключений .

И я уверен, что принуждать клиента к обработке исключений, проверяя их, - плохая практика. Он просто загрязняет ваш код или заставляет обернуть исключения во время выполнения или заставляет обрабатывать их вместо вызова, но не централизованно. Моя политика - избегать использования проверенных исключений, насколько это возможно. Рассмотрим IOException на Closable.close(): это полезно или удобно? Случаи, когда это очень редко, но каждый разработчик Java в мире вынужден иметь дело с этим. Чаще всего он проглатывается или регистрируется в лучшем случае. И представьте, сколько это добавляет к размеру кода ! Есть несколько сообщений о проверенных исключениях с их темной стороны:

  1. "Нужны ли Java проверенные исключения?" Брюс Экель
  2. Проблема с проверенными исключениями Беседа с Андерсом Хейлсбергом, Билл Веннерс с Брюсом Эккелем
  3. Недостатки Java Design, C2 wiki

Бывают случаи, когда на помощь приходят проверенные исключения. Но, на мой взгляд, они редки и обычно касаются реализации какого-то конкретного модуля. И они не приносят большой прибыли.

9 голосов
/ 05 февраля 2010

Я реализовал много итераторов, иногда поверх итераторов с проверкой-исключением (ResultSet концептуально является итератором записей, InputStream y концептуально итератором байтов) и так далее. Это очень, очень удобно и удобно (вы можете реализовать многоканальную архитектуру и фильтры для множества вещей).

Если вы предпочитаете объявить свои исключения , а затем объявить новый тип Iterator (ExceptionIterator, он будет похож на Runnable и Callable). Вы можете использовать его вместе со своим кодом, но не можете создавать его с внешними компонентами (библиотека классов Java или сторонние библиотеки).

Но если вы предпочитаете использовать суперстандартные интерфейсы (например, итератор), чтобы использовать их где угодно, то используйте Iterator. Если вы знаете, что ваши исключения будут условием прекращения обработки, или вы не против, используйте их.

Исключения во время выполнения не так страшны. К примеру. Hibernate использует их для реализации прокси и тому подобное. Они должны исключать исключения из БД, но не могут объявлять их в своих реализациях List.

3 голосов
/ 05 февраля 2010

Как человек, которому нравится Проверенные Java исключения, я думаю, что проблема (если хотите, недостаток дизайна Java) состоит в том, что стандартные библиотеки не поддерживают универсальный тип Iterator, когда метод next выбрасывает проверенное исключение.

База кода для Сезам имеет вариантный класс Iterator с именем Iterable , который делает именно это. Подпись следующая:

Interface Iteration<E,X extends Exception>

Type Parameters:
    E - Object type of objects contained in the iteration.
    X - Exception type that is thrown when a problem occurs during iteration.

Кажется, это работает в ограниченном контексте «Сезам», где используются определенные подклассы, которые «исправляют» параметр типа исключения. Но (конечно) он не интегрируется со стандартными типами коллекций, новым синтаксисом for цикла Java 5 и т. Д.

2 голосов
/ 05 февраля 2010

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

1 голос
/ 11 марта 2014

Если вы действительно не хотите использовать исключение времени выполнения, вы можете использовать NoSuchElementException. Вам разрешено использовать его в переопределенном методе next(), потому что он объявлен как брошенный в Iterator определение (я попробовал это и код скомпилирован). Но вы должны подумать, действительно ли это семантически действительно для вашего случая.

1 голос
/ 29 апреля 2012

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

public interface ExceptionHandler {
    // Log, or rethrow, or ignore...
    public Object handleException(Exception e);
}

Реализация классов может сделать что-то вроде этого:

public class ExceptionRethrower implements ExceptionHandler {
    @Override
    public Object handleException(Exception e) {
        throw new RuntimeException(e);
    }
}

public class ExceptionPrinter implements ExceptionHandler {
    @Override
    public Object handleException(Exception e) {
        System.err.println(e);
        return e;
    }
}

Для итератора, который читает из файла, код может выглядеть примерно так:

public class MyReaderIterator implements Iterator<String> {
    private final BufferedReader reader;
    private final ExceptionHandler exceptionHandler;
    public MyReaderIterator(BufferedReader r, ExceptionHandler h) {
        reader = r;
        exceptionHandler = h;
    }
    @Override
    public String next() {
        try {
            return reader.readLine();
        } catch (IOException ioe) {
            Object o = exceptionHandler.handleException(ioe);
            // could do whatever else with o
            return null;
        }
    }
    // also need to implement hasNext and remove, of course
}

Что думают люди? Это стоит такого уровня косвенности, или это расслоение нарушение и просто слишком сложно? Имя интерфейса может быть не совсем соответствующим (возможно, ExceptionProcessor или ExceptionInterceptor, так как он может взаимодействовать с исключением, но не полностью обрабатывать исключение, все же должна быть некоторая обработка в классе, в который он состоит).

Я согласен, что все это выглядит как обходной путь к тому факту, что Iterator.next () не был объявлен для создания исключений, и заставляет меня любить генераторы и модель исключений в python.

1 голос
/ 05 февраля 2010

Если ваша реализация не имеет свойств, требуемых интерфейсом Iterator, почему вы хотите ее использовать?

Я не вижу другого решения, кроме RuntimeException (с его проблемами).

0 голосов
/ 19 октября 2015

По моему мнению, итераторы предназначены для итерации , они не предназначены для вычисления , отсюда и отсутствие throws в методе next().

Вы спрашиваете, как использовать интерфейс для того, для чего он не предназначен.

Тем не менее, если Iterator предназначен для использования вами (что, как правило, является неверным предположением), вы могли быопределите общедоступный метод safeNext() следующим образом:

public Element safeNext() throws YourCheckedException {
    ...
}

@Override
public Element next() {
    try {
        return safeNext();
    } catch (YourException e) {
        throw new YourRuntimeException("Could not fetch the next element", e);
    }
}

Затем при использовании Iterator вы можете выбрать safeNext() и обработать проверенное исключение или использовать next(), как если бы вы использовалилюбые итераторы.

Короче говоря, вы не можете иметь как удобство интерфейса итератора (вы не можете ожидать, что объекты Iterable будут использовать метод safeNext()), так и выбрасывание проверенных исключений.

0 голосов
/ 20 ноября 2012

Я обычно использую

@Override
void remove() {
    throws new UnsupportedOperationException("remove method not implemented");
}

примечание: UnsupportedOperationException является RuntimeException

Так как вы правильно стремитесь к чему-то вроде

for (IterElem e : iterable)
    e.action();

С тобой все будет в порядке

0 голосов
/ 05 февраля 2010

Что бы вы ни делали, не используйте этот трюк: -)

http://blog.quenta.org/2006/11/generic-exceptions.html

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...