Существует ли какой-либо официальный контракт для интерфейса Iterable в отношении множественного использования?

Начиная с Java 5, у нас есть новый тип java.lang.Iterable, который можно использовать в циклах foreach как таковых:

for (Object element : iterable);

Контракт Iterable не указывает, будет ли его iterator() можно вызывать более одного раза перед удалением итерации. I.e., неясно, можно ли ожидать, что следующее будет работать для всех Iterables:

for (Object element : iterable);
for (Object element : iterable);

Например, реализация Iterator wrapping не может использоваться дважды:

public class OneShotIterable<T> implements Iterable<T> {
    private final Iterator<T> it;

    public OneShotIterable(Iterator<T> it) {
        this.it = it;
    }

    @Override
    public Iterator<T> iterator() {
        return it;
    }
}

Для большинства Iterables это не имеет значения, поскольку они на самом деле являются ретро-установленными типами API коллекции, такими как List, Set, которые уже имеют четко определенные контракты для своих методов iterator().

Мой вопрос: Является ли реализация OneShotIterable нарушением какого-либо контракта, который я пропускаю? Другими словами, будут ли пользователи Iterable ожидать, что они будут повторно использоваться? Если да, существует ли "официальная" рекомендация экспертной группы Java 5, как бороться с таким "одним выстрелом" Iterables (например, выбросить IllegalStateException при втором вызове)?

Ответ 1

Одним из прецедентов, которые я мог найти в стандартной библиотеке, является интерфейс DirectoryStream.

В его Джавадоке содержится следующий отрывок (акцент их):

Пока DirectoryStream extends Iterable, это не универсальный Iterable, поскольку он поддерживает только один Iterator; вызывая метод Iterator для получения второго или последующего итератора, бросает IllegalStateException.

Для меня это предполагает две вещи:

  • Подразумеваемый контракт на Iterable заключается в том, что вы должны иметь возможность повторять несколько раз (возможно, даже одновременно!)
  • Предупреждение жирным шрифтом в документации в сочетании с метанием IllegalStateException, вероятно, является лучшим способом справиться с несоблюдением в ваших собственных классах/интерфейсы.

Ответ 2

Не совсем ответ на мой вопрос, но Apache Commons Collections обратился к этой проблеме в своем классе IteratorUtils:

Оба являются фабричными методами для типа IteratorIterable. Вышеупомянутое различие проясняет, что нужно быть осторожным, оборачивая Iterators в Iterables.