为什么Scanner会实现Iterator <String>?

Thi*_*thi 11 java oop design-patterns java.util.scanner

我只是想知道为什么java.util.Scanner实现了java.util.Iterator

Scanner实现remove方法并抛出UnsupportedOperationException.

但是,在实现接口时,不应该是一个类,履行接口的契约?

实现iterator和添加抛出异常的方法有什么用?

为什么不避免接口的实现并保持简单?

有人可以争辩说,它被定义为可以扩展的类Scanner可以实现该方法,就像AbstractList有一个抛出一个的add方法UnsupportedOperationException.但是AbstractList是一个abstract班级,而是Scanner一个final班级.

这不是一个糟糕的设计实践吗?

Vin*_*igh 9

我会说是的,Iterator有一个设计缺陷,并将其抛入与尝试创建不可变Collection实现相同的类别.

它违反了接口隔离原则,并强制开发人员将一个角落案例包含在JavaDocs(臭名昭着UnsupportedOperationException)中,以避免违反Liskov Subsitution Principle.你也可以在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> {

}
Run Code Online (Sandbox Code Playgroud)

绝对可以使用更好的名字.由于命名选择不当,请不要关闭这篇文章.

为什么Scanner执行Iterator摆在首位?

Scanner在遍历集合的意义上,它不是迭代器.但是a的想法Scanner是提供它被" 扫描 "的输入,这在某种意义上迭代某些东西(a中的字符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);
}
Run Code Online (Sandbox Code Playgroud)

但是如果JDK支持一个Traversable类型并允许增强循环来接受Traversable项目,这也会有效,因为以这种方式从集合中删除可能会抛出一个ConcurrentModificationException,这导致使用迭代器.

结论

那么好的设计呢?不.它违反了ISP,导致合同混乱.这只是一种典型的代码味道.真正的问题是语言缺乏对不变性的支持,这应该允许开发人员指定行为是否应该改变状态,允许行为合同被剥夺其可变性.或类似的规定..

JDK充满了这样的东西(糟糕的设计选择,比如暴露length数组和ImmutableMap上面提到的尝试),现在更改它会导致代码破坏.

  • 你的答案非常清晰.非常感谢您的详细解释.:) (3认同)