Why does Scanner implement Iterator?

前端 未结 4 1871
余生分开走
余生分开走 2021-01-04 08:48

I was just wondering why java.util.Scanner implements java.util.Iterator?

Scanner implements the remove method and throws an UnsupportedOperationExcept

4条回答
  •  时光说笑
    2021-01-04 09:26

    I'd say yes, it's a design flaw. The flaw is within Iterator. This issue could be thrown in the same category as attempting to create an immutable Collection implementation.

    It violates the Interface Segregation Principle and forces the developers to include a corner case into the JavaDocs (the infamous UnsupportedOperationException) to avoid violating the Liskov Subsitution Principle. You'll find this in Collection#remove methods aswell.


    I believe design could be improved by decomposing the interface, segregating hasNext() and next() into a new (immutable) interface and letting the (mutable) Iterator interface derive from it :

    interface Traversable {
        boolean hasNext();
        E next();
    }
    
    interface Iterator extends Traversable {
        void remove();
    }
    
    final class Scanner implements Traversable {
    
    }
    

    Better names could definitely be used. Please don't down this post due to my bad naming choices.

    Why does Scanner implement Iterator in the first place?

    Scanner is not an iterator in the sense of traversing a collection. But the idea of a Scanner is to supply it input to be "scanned", which in a sense is iterating over something (the characters in a String).

    I can see why Scanner would implement Iterator (you were asking for a use case). For example, if you wanted to create your own Iterable type to iterate over a String specifying a delimiter:

    class ScannerWrapper implements Iterable {
        public Scanner scanner;
    
        public ScannerWrapper(Scanner scanner) {
            this.scanner = scanner;
        }
    
        public Iterator 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);
    }
    

    But this would have also worked if the JDK supported a Traversable type and allowed enhanced loops to accept Traversable items, since removing from a collection in this fashion may throw a ConcurrentModificationException, which leads to using an iterator instead.

    Conclusion

    So is it good design? Nope. It violates ISP and results in cluttered contracts. This is simply a giagantic code smell. The real problem is the language's lack of support for immutability, which should allow developers to specify whether a behavior should mutate state, allowing behavioral contracts to be stripped of their mutability. Or something along those lines..

    The JDK is filled with stuff like this (bad design choices, such as exposing length for arrays and attempts at an ImmutableMap I mentioned above), and changing it now would result in breaking code.

提交回复
热议问题