我只是想知道为什么 java.util.Scanner工具 java.util.Iterator ?
Scanner
实现了 remove方法并抛出 UnsupportedOperationException .
但是类在实现接口(interface)时不应该履行接口(interface)的契约吗?
实现iterator
并添加一个抛出异常的方法有什么用?
为什么不直接避免接口(interface)的实现并保持简单呢?
有人可能会争辩说,它的定义是为了让可能扩展 Scanner
的类可以实现该方法,例如 AbstractList有一个 add抛出 UnsupportedOperationException
的方法。但是 AbstractList
是一个 abstract
类,而 Scanner
是一个 final
类。
这不是一个糟糕的设计实践吗?
最佳答案
我会说是的,这是一个设计缺陷。该缺陷存在于 Iterator
中。此问题可能与尝试创建不可变的 Collection
实现属于同一类别。
它违反了Interface Segregation Principle并强制开发人员包含 corner case进入JavaDocs (臭名昭著的 UnsupportedOperationException
)以避免违反 Liskov Subsitution Principle .您会在 Collection#remove
中找到它方法也是如此。
我相信可以通过分解接口(interface)、将 hasNext()
和 next()
分离到一个新的(不可变的)接口(interface)并让(可变的)Iterator
接口(interface)派生自它:
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
的想法是为其提供“scanned”输入,这在某种意义上是迭代某些东西(中的字符一个 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
,导致改用迭代器。
结论
那么它是好的设计吗?没有。它违反了 ISP 并导致契约(Contract)困惑。这简直就是一种巨大的代码味道。真正的问题是该语言缺乏对不变性的支持,这应该允许开发人员指定行为是否应该改变状态,从而允许行为契约被剥夺其可变性。或者类似的东西..
JDK 充满了这样的东西(糟糕的设计选择,例如为数组公开 length
并尝试我上面提到的 ImmutableMap
),现在改变它会导致破坏代码。
关于java - Scanner 为什么要实现 Iterator<String>?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31153006/