为什么Scanner实现Iterator <string>?

时间:2015-07-01 05:15:07

标签: java oop design-patterns java.util.scanner

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

Scanner实施remove方法并抛出UnsupportedOperationException

但是,在实现接口时,不应该是一个类来完成接口的合同吗?

实现iterator并添加引发异常的方法有什么用?

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

有人可以争辩说,它被定义为可以扩展Scanner的类可以实现该方法,例如AbstractListadd方法抛出UnsupportedOperationException。但AbstractListabstract类,而Scannerfinal类。

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

4 个答案:

答案 0 :(得分:8)

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

它违反Interface Segregation Principle并强制开发人员在corner case(臭名昭着的UnsupportedOperationException)中加入JavaDocs以避免违反Liskov Subsitution Principle。您也可以在Collection#remove方法中找到它。

我相信设计可以通过分解界面,将hasNext()next()分隔成一个新的(不可变的)接口并让(可变的)Iterator接口派生出来来改进设计:< / p>

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的想法是将输入提供为&#34; 扫描&#34;这在某种意义上迭代某些东西( 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,导致合同混乱。这只是一种典型的代码味道。真正的问题是语言缺乏对不变性的支持,这应该允许开发人员指定行为是否应该改变状态,允许行为合同被剥夺其可变性。或者沿着这些方向的东西..

JDK充满了这样的东西(糟糕的设计选择,例如为数组公开length并尝试上面提到的ImmutableMap),现在更改它会导致代码破坏。

答案 1 :(得分:3)

因为实现迭代器允许在只能使用只读迭代器的地方使用扫描器。

此外,它确实实施了合同。从Iterator文档(强调我的):

删除() 从底层集合中删除此迭代器返回的最后一个元素(可选操作)

答案 2 :(得分:0)

这不是一种受支持的方法。为了避免以这种方式实现它,需要一个没有remove方法的类似接口。创建多个类似接口只是为了避免抛出NotImplementedException的方法?

,这是一个好的设计吗?

任何使用Scanner的人都知道(或很快就会知道)remove()方法无法使用,所以它确实没有实际效果。它也可能是一个无操作,因为 项有效删除,只是因为remove(),但是抛出异常可能更清楚。

答案 3 :(得分:0)

要获得正式答案,请参阅Collections Framework Overview。向下滚动到设计目标

  

为了保持核心接口的数量较少,接口不会   试图捕捉可变性这样微妙的区别,   可修改性和可恢复性。相反,核心中的某些调用   接口是可选,使实现能够抛出   UnsupportedOperationException表示他们不支持a   指定的可选操作。收集实施者必须清楚   记录实施支持哪些可选操作。

正如之前的答案所指出的那样,Collections框架可以通过将其接口分解为众多更小的接口来维护Liskov Substition。考虑并拒绝了这种方法,以便最大限度地减少框架中核心接口的数量。