/ / Por que o Scanner implementa o Iterator <>? - java, oop, padrões de design, java.util.scanner

Por que o Scanner implementa o Iterator <>? - java, oop, padrões de design, java.util.scanner

Eu só estava me perguntando por que java.util.Scanner implementa java.util.Iterator?

Scanner implementa o remover método e lança um UnsupportedOperationException.

Mas uma classe, ao implementar uma interface, não deveria cumprir o contrato da interface?

Qual é a utilidade de implementar iterator e adicionando um método que lança uma exceção?

Por que não evitar a implementação da interface e mantê-la simples?

Pode-se argumentar que está definido para que a classe que pode estender Scanner poderia implementar o método, como AbstractList tem um adicionar método que lança um UnsupportedOperationException. Mas AbstractList é um abstract classe, enquanto Scanner é um final classe.

Isso não é uma prática ruim de design?

Respostas:

6 para resposta № 1

Eu diria sim, Iterator tem uma falha de design e a lança na mesma categoria que tentar criar uma imutável Collection implementação.

Viola o Princípio de Segregação de Interface e força os desenvolvedores a incluir um caso de canto no JavaDocs (os infames UnsupportedOperationException) para evitar violar o Princípio da subscrição de Liskov. Você encontrará isso em Collection#remove métodos também.

Eu acredito que o design pode ser melhorado decompondo a interface, segregando hasNext() e next() em uma nova interface (imutável) e deixando o (mutável) Iterator a interface deriva disso:

interface Traversable<E> {
boolean hasNext();
E next();
}

interface Iterator<E> extends Traversable<E> {
void remove();
}

final class Scanner implements Traversable<String> {

}

Definitivamente, nomes melhores poderiam ser usados. Por favor, não publique esta postagem devido às minhas escolhas de nomes incorretos.

Scanner não é um iterador no sentido de percorrer uma coleção. Mas a ideia de um Scanner é fornecer entrada para ser "digitalizado", que em certo sentido é iterando sobre algo (os caracteres em um String).

Eu posso ver porque Scanner implementaria Iterator (você estava solicitando um caso de uso). Por exemplo, se você quiser criar seu próprio Iterable digite para iterar sobre um String especificando um delimitador:

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);
}

Mas isso também teria funcionado se o JDK suportasse um Traversable digite e permita que loops aprimorados aceitem Traversable itens, pois remover de uma coleção dessa maneira pode causar ConcurrentModificationException, o que leva ao uso de um iterador.

Conclusão

Então, é um bom design? Não. Ele viola o ISP e resulta em contratos desordenados. Isso é simplesmente um cheiro de código giagantic. O problema real é a falta de suporte à imutabilidade da linguagem, o que deve permitir que os desenvolvedores especifiquem se um comportamento deve sofrer mutação de estado, permitindo que os contratos comportamentais sejam removidos de sua mutabilidade. Ou algo nesse sentido.

O JDK é preenchido com coisas assim (más escolhas de design, como expor length para matrizes e tentativas de ImmutableMap Mencionei acima) e alterá-lo agora resultaria na quebra de código.


3 para resposta № 2

Porque a implementação do iterador permite que o scanner seja usado sempre que um iterador somente leitura puder ser usado.

Além disso, implementa o contrato. Na documentação do Iterator (ênfase minha):

retirar() Remove da coleção subjacente o último elemento retornado por este iterador (operação opcional).


0 para resposta № 3

Não é apenas um método suportado. Para evitar implementá-lo dessa maneira, seria necessária uma interface semelhante sem um método de remoção. É bom criar várias interfaces semelhantes apenas para evitar um método que lança NotImplementedException?

Qualquer pessoa usando um Scanner sabe (ou em breve saberá) que o remove() O método não pode ser usado, por isso realmente não tem efeito prático. Também pode ser um não-op, pois efetivamente o item é removido, mas não devido a remove(), mas provavelmente é mais claro lançar uma exceção.


0 para a resposta № 4

Para uma resposta oficial, consulte o Visão geral da estrutura de coleções. Role para baixo até a parte inferior Objetivos do projeto.

Para manter pequeno o número de interfaces principais, ointerfaces não tentar capturar distinções sutis como mutabilidade, modificabilidade e redimensionabilidade. Em vez disso, certas chamadas no núcleo interfaces são opcional, permitindo implementações para lançar um UnsupportedOperationException para indicar que eles não suportam um operação opcional especificada. Os implementadores de coleção devem claramente documentar quais operações opcionais são suportadas por uma implementação.

Como as respostas anteriores apontaram, oA estrutura de coleções pode sustentar a Substituição Liskov decompondo suas interfaces em inúmeras interfaces menores. Essa abordagem foi considerada e rejeitada, a fim de minimizar o número de interfaces principais na estrutura.