Ответ 1
Я бы сказал, да, это недостаток дизайна. Это Iterator
, который имеет недостаток. Эта проблема может быть отнесена к той же категории, что и попытка создать неизменную реализацию Collection
.
Это нарушает принцип сегрегации интерфейса и вынуждает разработчиков включать angular случай в JavaDocs (печально известный UnsupportedOperationException
), чтобы избежать нарушения Принцип замещения Лискова. Вы найдете это также в Collection#remove
методах.
Я полагаю, что дизайн может быть улучшен путем разложения интерфейса, разделения hasNext()
и next()
на новый (неизменяемый) интерфейс и предоставления возможности (изменяемого) интерфейса Iterator
получить его:
interface Traversable<E> {
boolean hasNext();
E next();
}
interface Iterator<E> extends Traversable<E> {
void remove();
}
final class Scanner implements Traversable<String> {
}
Определенно можно использовать лучшие имена. Пожалуйста, не закрывайте этот пост из-за моего неправильного выбора имен.
Почему Scanner
реализует Iterator
в первую очередь?
Scanner
не является итератором в смысле обхода коллекции. Но идея Scanner
состоит в том, чтобы предоставить его вход для "сканирования", которое в некотором смысле перебирает что-то (символы в String
).
Я понимаю, почему Scanner
будет реализовывать Iterator
(вы запрашивали вариант использования). Например, если вы хотите создать свой собственный тип Iterable
для итерации по String
с указанием разделителя:
class ScannerWrapper implements Iterable<E> {
public Scanner scanner;
public ScannerWrapper(Scanner scanner) {
this.scanner = scanner;
}
public Iterator<String> iterator() {
return scanner;
}
}
Scanner scanner = new Scanner("one,two,three");
scanner.useDelimiter(",");
ScannerWrapper wrapper = new ScannerWrapper(scanner);
for(String s : wrapper) {
System.out.println(s);
}
Но это также сработало бы, если бы JDK поддерживал тип Traversable
и позволял расширенным циклам принимать элементы Traversable
, так как удаление из коллекции таким способом может вызвать ConcurrentModificationException
, что приводит к использованию итератора вместо этого.
Заключение
Так это хороший дизайн? Нет. Это нарушает провайдера и приводит к загроможденным контрактам. Это просто гигантский запах кода. Настоящей проблемой является отсутствие поддержки неизменяемости языка, что должно позволить разработчикам указать, должно ли поведение изменять состояние, позволяя лишать поведенческие контракты их изменчивости. Или что-то в этом роде..
JDK заполнен такими вещами (плохой выбор дизайна, такой как выставление length
для массивов и попытки ImmutableMap
, о котором я упоминал выше), и изменение его сейчас приведет к нарушению кода.