Что делать с исключениями при реализации java.lang.Iterator

Интерфейс 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);
        }
    }

    ...
}

person Vincent Robert    schedule 05.02.2010    source источник


Ответы (10)


Я реализовал множество Iterator, иногда поверх итераторов with-check-exception (ResultSet концептуально является итератором записи, InputStream y концептуально является итератором байтов) и так далее. Это очень, очень приятно и удобно (вы можете реализовать архитектуру конвейера и фильтров для МНОГОЕГО).

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

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

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

person helios    schedule 05.02.2010
comment
Другой вариант, если вы не можете управлять клиентским кодом пользовательского интерфейса: создайте итератор, который регистрирует исключения в любом месте, доступном для геттера или чего-то еще, или вызовите интерфейс обратного вызова или что-то, что вы контролируете, и может отображать ошибку где угодно. Если это полезно для вас, вы можете создать CatcherIterator, который просто обертывает runtime-exception-thrower-iterator и перехватывает все возможные исключения, перенаправляя их в свойство или интерфейс обратного вызова. - person helios; 05.02.2010

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

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

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

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

person Rorick    schedule 05.02.2010

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

В базе кода для Sesame есть класс варианта 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.

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

person Stephen C    schedule 05.02.2010

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

person ishmeister    schedule 05.02.2010

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

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

person Marcel Jackwerth    schedule 05.02.2010

Этот вопрос был задан давно, но я хотел бы получить отзывы о шаблоне, который может быть здесь полезен. Дополнительный интерфейс или класс 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.

person bpow    schedule 29.04.2012

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

person nuoritoveri    schedule 11.03.2014
comment
Я думаю, что это лучшее исключение для ошибок итератора - person smac89; 01.07.2015
comment
NoSuchElementException - это RuntimeException. Вы также можете создать собственное расширение для RuntimeException, оно будет таким же, верно? - person boumbh; 19.10.2015

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

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

person Community    schedule 05.02.2010

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

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

примечание: UnsupportedOperationException - это исключение времени выполнения

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

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

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

person smnsvane    schedule 20.11.2012

На мой взгляд, итераторы предназначены для итерации, они не предназначены для вычислений, отсюда и отсутствие 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()) и генерирования проверенного исключения.

person boumbh    schedule 19.10.2015