Dlaczego potrzebujemy wystąpienia klasy skanera, aby uzyskać dane wejściowe w Javie?

10

Java jest zorientowana obiektowo, ale dlaczego musimy utworzyć obiekt z klasy skanera, aby uzyskać dane wejściowe? Czy next()na przykład metody nie mogą być po prostu statyczne?

C wygląda mi na prostsze, jak tylko używasz scanf(), gets()lub fgets(). Jestem pewien, że istnieje powód dla programistów Java, aby utworzyć klasę Scanner, ale jak to jest lepsze niż zwykła funkcja do wykonywania pracy?

Znalazłem ten link, który może wydawać się zadawać to samo pytanie, ale odpowiedzi już są

„musisz utworzyć obiekt, ponieważ nie jest on statyczny” ...

Domyślam się, że skoro Java jest zorientowana obiektowo, postanowili umieścić wszystkie metody wprowadzania w klasie. Nie zrobili metod statycznych, więc możesz mieć różnego rodzaju źródła (wejście klawiatury, wejście pliku ...) w różnych obiektach?

Byłbym wdzięczny, gdyby ktoś mógł edytować pytanie, aby brzmiało to wyraźniej!

Pablito
źródło

Odpowiedzi:

34

Odpowiedź brzmi „ponieważ skaner ma stan”.

Patrząc na kod java.util.Scanner , zobaczysz szereg prywatnych pól, takich jak bufor i powiązane z nim informacje, Matcher, Wzorzec, źródło wejściowe, informacje o tym, czy źródło jest zamknięte, czy nie, typ ostatniej pasującej rzeczy, informacja o tym, czy ostatnia rzecz była zgodna, czy nie, podstawa użyta dla liczb, ustawienia regionalne (informacja o tym, czy używasz separatora tysięcy, .czy ,też), a także własna pamięć podręczna LRU dla ostatnio używanych wzorców , informacje o ostatnim napotkanym wyjątku, niektóre informacje o analizowaniu liczb, niektóre informacje o analizowaniu boolanów, trochę więcej informacji o analizowaniu liczb całkowitych ... i myślę, że o to chodzi.

Jak widać, jest to dość duży blok tekstu. Taki jest stan skanera. Aby zmienić skaner w klasę statyczną, stan ten musiałby być zapisany gdzie indziej. Sposób, w jaki robi to C, naprawdę nie ma z tym aż tak wielkiego stanu. Masz fscanf. PLIK utrzymuje pewien stan dotyczący pozycji, w której się znajduje (ale należy to przekazać przy każdym wywołaniu fscanf). Jeśli wystąpił błąd, trzeba go przetworzyć (i wtedy zaczniesz kod, który wygląda jak pisanie to ) - i że nie mówi ci informacje, takie jak „Spodziewałem się liczbą całkowitą, ale znalazł String”.

Gdy spojrzymy na teoretycznie statyczny skaner - cały stan jest utrzymywany poza klasą, nie jest on zamknięty w klasie. Inne fragmenty kodu mogą majstrować przy tych zmiennych. Kiedy inny kod może majstrować przy stanie klasy, bardzo trudno jest zrozumieć, co klasa zrobi w danej sytuacji.

Być może możesz napisać coś takiego ScannerState { Locale loc; ... }i mieć kod, który spowoduje:

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

Ale wtedy jest to o wiele bardziej kłopotliwe niż stan enkapsulowany w obiekcie skanera w pierwszej kolejności (i nie trzeba przechodzić w stan).

Na koniec skaner implementuje interfejs, Iterator<String>dzięki czemu można go używać w kodzie, takim jak:

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

Bez możliwości uzyskania instancji klasy Scanner ten typ struktury staje się bardziej niewygodny w języku zorientowanym obiektowo.

Społeczność
źródło
1
Wszystko, co napisałeś, jest absolutnie prawdziwe, chociaż InputStream ma również stan, nie tylko skaner. Jeśli dane wejściowe pochodzą z konsoli, podobnie jak w C, nie trzeba przekazywać żadnych parametrów, aby rozpocząć pobieranie danych. Przypuszczam, że to było zrobione w ten sposób, aby być zgodne z tym, jak inne strumienie są wykonywane które nie wymagają państwo.
Neil
@ Neil InputStream to FILE*(stan pozycji) w C.
maniak zapadkowy
1
Narzędzie skanera Iterator- nie Iterable. Nie można używać skanera w pętli rozszerzonej.
turbanoff
@ratchetfreak Dokładnie. Właśnie to musi mieć „stan” FileInputStreams, ale nie dotyczy to danych wejściowych z konsoli, ponieważ jest już technicznie otwarty.
Neil,
1
@turbanoff Dziękuję za zaproszenie mnie do tego. Poprawiłem to.
7

krótka odpowiedź: nie. Możesz uzyskać dane wejściowe od użytkownika bez użycia instancji skanera.
Na przykład: https://docs.oracle.com/javase/tutorial/essential/io/cl.html lub
http://alvinalexander.com/blog/post/java/java-source-code-read-command-line -Wejście

jwenting
źródło
String orgName = (new BufferedReader(new InputStreamReader(System.in))).readLine();Jest to strasznie skomplikowane w porównaniu do używania a, Scannera także tworzy nowe wystąpienia nie jednego, ale dwóch obiektów, aby od razu je odrzucić.
Philipp
2
@Philipp, 1) Jest to uciążliwe, ale z pewnością jest alternatywą, i 2) jeśli od razu odrzucasz instancje, robisz coś złego (lub naprawdę potrzebujesz tylko odczytać jedną linię z konsoli).
Arturo Torres Sánchez
Nie potrzebujesz skanera do odczytu danych wejściowych. Nie potrzebujesz również InputStreamReader i nie potrzebujesz BufferedReader. Możesz pracować ze „surowym” strumieniem w System.in, tak jak w C. Skaner to po prostu bardzo wygodny sposób na zużywanie tego strumienia.
Traubenfuchs