Pourquoi avons-nous besoin d'une instance de la classe Scanner pour obtenir une entrée sur Java?

10

Java est orienté objet, mais pourquoi avons-nous besoin de créer un objet à partir de la classe Scanner pour obtenir une entrée? Les next()méthodes, par exemple, ne pourraient-elles pas simplement être statiques?

C me semble assez simple à utiliser scanf(), gets()ou fgets(). Je suis sûr qu'il y a une raison pour les développeurs Java de créer la classe Scanner, mais en quoi est-ce mieux que d'avoir simplement une fonction normale pour faire le travail?

J'ai trouvé ce lien qui peut sembler poser la même question, mais les réponses sont à peu près

"vous devez créer un objet car il n'est pas statique" ...

Ma conjecture est: comme Java est orienté objet, ils ont décidé de mettre toutes les méthodes d'entrée dans une classe. Ils n'ont pas fait de méthodes statiques donc vous pouvez avoir toutes sortes de sources différentes (entrée clavier, entrée fichier ...) dans différents objets?

J'apprécierais que quelqu'un puisse modifier la question pour la rendre plus claire!

Pablito
la source

Réponses:

34

La réponse est "parce qu'un scanner a un état".

En regardant le code de java.util.Scanner , vous verrez un certain nombre de champs privés tels qu'un tampon et ses informations associées, un Matcher, un Pattern, une source d'entrée, des informations sur si la source est fermée ou non, le type de la dernière chose trouvée, des informations sur si la dernière chose était une correspondance valide ou non, la base utilisée pour les nombres, les paramètres régionaux (informations sur si vous utilisez .ou ,comme séparateur de milliers) et son propre cache LRU pour les modèles récemment utilisés , les informations sur la dernière exception qui a été rencontrée, quelques informations sur l'analyse des nombres, des informations sur l'analyse des booléens, un peu plus d'informations sur l'analyse des entiers ... et je pense que c'est tout.

Comme vous pouvez le voir, c'est un bloc de texte assez important. Tel est l'état du scanner. Afin de faire du Scanner une classe statique, cet état devrait être stocké ailleurs. La manière C de le faire n'a vraiment pas beaucoup d'état avec elle. Vous en avez un fscanf. Le FICHIER conserve un état sur la position à laquelle il se trouve (mais cela doit être transmis pour chaque invocation de fscanf). S'il y a eu une erreur, vous devez la traiter (et ensuite vous commencez à écrire du code qui ressemble à ceci ) - et cela ne vous dit pas des informations comme "je m'attendais à un entier, mais j'ai trouvé une chaîne".

Quand on regarde le scanner théoriquement statique - tout l'état est maintenu en dehors de la classe, il n'est pas encapsulé dans la classe. D'autres bits de code pourraient bricoler ces variables. Lorsqu'un autre code peut bricoler l'état de la classe, il devient très difficile de raisonner sur ce que la classe fera dans une situation donnée.

Vous pourriez peut-être écrire quelque chose comme ScannerState { Locale loc; ... }et avoir du code qui se traduit par:

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

Mais alors, c'est beaucoup plus lourd que d'avoir l'état encapsulé dans un objet Scanner en premier lieu (et de ne pas avoir besoin de passer l'état).

Enfin, le Scanner implémente l'interface Iterator<String>ce qui signifie que l'on peut l'utiliser en code tel que:

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

Sans pouvoir obtenir une instance de la classe Scanner, ce type de structure devient plus encombrant dans un langage orienté objet.

Communauté
la source
1
Tout ce que vous avez écrit est absolument vrai, même si InputStream a également un état, pas seulement Scanner. Si l'entrée provient de la console, comme en C, vous n'avez pas besoin de passer de paramètres pour commencer à prendre l'entrée. Je suppose que cela a été fait de cette façon d'être cohérent avec la façon dont d' autres cours d' eau sont faites qui ne nécessitent état.
Neil
@Neil InputStream équivaut à FILE*(l'état de position) en C.
ratchet freak
1
Le scanner met en œuvre Iterator- non Iterable. Il est impossible d'utiliser Scanner en boucle améliorée.
turbanoff
@ratchetfreak précisément. C'est ce que "l'état" FileInputStreams doit avoir, mais il ne s'applique pas aux entrées de la console car il est déjà techniquement ouvert.
Neil
1
@turbanoff Merci de m'avoir appelé à ce sujet. Je l'ai corrigé.
7

réponse courte: non. Vous pouvez obtenir une entrée utilisateur sans utiliser d'instance de scanner.
Par exemple: https://docs.oracle.com/javase/tutorial/essential/io/cl.html ou
http://alvinalexander.com/blog/post/java/java-source-code-read-command-line -contribution

jwenting
la source
String orgName = (new BufferedReader(new InputStreamReader(System.in))).readLine();C'est horriblement alambiqué par rapport à l'utilisation d'un Scanneret crée également de nouvelles instances non pas d'un mais de deux objets juste pour les jeter immédiatement.
Philipp
2
@Philipp, 1) C'est lourd, mais c'est certainement une alternative, et 2) si vous supprimez les instances tout de suite, vous faites quelque chose de mal (ou vous n'avez vraiment besoin de lire qu'une seule ligne de la console).
Arturo Torres Sánchez
Vous n'avez pas besoin du scanner pour lire l'entrée. Vous n'avez pas non plus besoin d'un InputStreamReader et vous n'avez pas besoin d'un BufferedReader. Vous pouvez travailler avec le flux "brut" sur System.in, tout comme dans C. Scanner est tout simplement un moyen très confortable de consommer ce flux.
Traubenfuchs