Подтвердить что ты не робот

Что делать с исключениями при реализации 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);
        }
    }

    ...
}
4b9b3361

Ответ 1

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

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

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

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

Ответ 2

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

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

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

Ответ 3

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

Ответ 4

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

Ответ 5

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

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

Ответ 6

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

Ответ 7

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

Ответ 9

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

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

note: UnsupportedOperationException - исключение RuntimeException

Так как вы хорошо нацелены на что-то вроде

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

Вы будете в порядке

Ответ 10

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