¿Por qué necesitamos una instancia de la clase Scanner para obtener una entrada en Java?

10

Java está orientado a objetos, pero, ¿por qué necesitamos crear un objeto desde la clase Scanner para obtener entrada? ¿No podrían los next()métodos, por ejemplo, ser simplemente estáticos?

C me parece bastante más simple, ya que solo usas scanf(), gets()o fgets(). Estoy seguro de que hay una razón para que los desarrolladores de Java creen la clase Scanner, pero ¿cómo es mejor que solo tener una función normal para hacer el trabajo?

He encontrado este enlace que puede parecer que hace la misma pregunta, pero las respuestas son casi

"necesitas crear un objeto porque no es estático" ...

Mi suposición es: dado que Java está orientado a objetos, decidieron poner todos los métodos de entrada en una clase. ¿No hicieron métodos estáticos para que pueda tener todo tipo de fuentes diferentes (entrada de teclado, entrada de archivo ...) en diferentes objetos?

Agradecería que alguien pueda editar la pregunta para que suene más clara.

Pablito
fuente

Respuestas:

34

La respuesta es "porque un escáner tiene estado".

Al mirar el código de java.util.Scanner , verá una serie de campos privados como un búfer y su información asociada, un Matcher, un Patrón, una fuente de entrada, información sobre si la fuente está cerrada o no, el tipo de lo último que coincidió, información sobre si lo último fue una coincidencia válida o no, la raíz utilizada para los números, la configuración regional (información sobre si está utilizando .o ,como un separador de miles) y su propio caché LRU para patrones utilizados recientemente , la información sobre la última excepción que se encontró, alguna información sobre el análisis de números, alguna información sobre el análisis de booleanos, bastante más información sobre el análisis de enteros ... y creo que eso es todo.

Como puede ver, ese es un bloque de texto bastante grande allí. Ese es el estado del escáner. Para convertir el escáner en una clase estática, ese estado debería almacenarse en otro lugar. La forma C de hacerlo realmente no tiene tanto estado. Tienes un fscanf. El ARCHIVO mantiene algún estado acerca de la posición en la que se encuentra (pero eso debe pasarse para cada invocación de fscanf). Si hubo un error, hay que procesarlo (y después de empezar a escribir código que se parece a esto ) - y que no le dicen la información como "Me esperaba un entero, pero encontró una cadena".

Cuando uno mira el escáner teóricamente estático: todo el estado se mantiene fuera de la clase, no está encapsulado dentro de la clase. Otros bits de código podrían jugar con esas variables. Cuando otro código puede jugar con el estado de la clase, se hace muy difícil razonar sobre lo que la clase hará en una situación dada.

Podría, tal vez, escribir algo así ScannerState { Locale loc; ... }y tener un código que resulte en:

ScannerState state = new ScannerState(a whole lot of arguments);
int foo = Scanner.nextInt(state);

Pero entonces, esto es mucho más engorroso que tener el estado encapsulado dentro de un objeto Scanner en primer lugar (y sin necesidad de pasar en el estado).

Por último, el escáner implementa la interfaz, lo Iterator<String>que significa que se puede usar en código como:

Scanner in = new Scanner(someFile);
whie(in.hasNext()) { ... }

Sin poder obtener una instancia de la clase Scanner, este tipo de estructura se vuelve más engorroso dentro de un lenguaje orientado a objetos.

Comunidad
fuente
1
Todo lo que escribió es absolutamente cierto, aunque InputStream también tiene estado, no solo Scanner. Si la entrada proviene de la consola, como en C, no necesita pasar ningún parámetro para comenzar a tomar la entrada. Supongo que se hace de esta manera para ser coherente con cómo otras corrientes se hacen, que no requiere el estado.
Neil
@Neil InputStream equivale a la FILE*(estado de posición) en C.
trinquete monstruo
1
El escáner implementa Iterator- no Iterable. Es imposible usar el escáner en bucle mejorado.
turbanoff
@ratchetfreak Precisamente. Eso es lo que debe tener FileInputStreams de "estado", pero no se aplica a la entrada de la consola ya que técnicamente está abierto.
Neil
1
@turbanoff Gracias por llamarme por eso. Lo he corregido.
7

respuesta corta: no lo haces. Puede obtener la entrada del usuario sin usar una instancia de escáner.
Por ejemplo: https://docs.oracle.com/javase/tutorial/essential/io/cl.html o
http://alvinalexander.com/blog/post/java/java-source-code-read-command-line -entrada

jwenting
fuente
String orgName = (new BufferedReader(new InputStreamReader(System.in))).readLine();Eso es horriblemente complicado en comparación con el uso de a Scannery también crea nuevas instancias de no uno sino dos objetos solo para descartarlos de inmediato.
Philipp
2
@Philipp, 1) Es engorroso, pero ciertamente es una alternativa, y 2) si está descartando las instancias de inmediato, está haciendo algo mal (o realmente solo necesita leer una línea desde la consola).
Arturo Torres Sánchez
No necesita el escáner para leer la entrada. Tampoco necesita un InputStreamReader y no necesita un BufferedReader. Puede trabajar con la transmisión "sin procesar" en System.in, al igual que en C. Scanner es simplemente una forma muy cómoda de consumir esa transmisión.
Traubenfuchs