Зачем нам нужен экземпляр класса Scanner для получения входных данных на Java?

10

Java является объектно-ориентированным, но зачем нам нужно создавать объект из класса Scanner для получения ввода? next()Например, не могут ли методы быть статическими?

C выглядит для меня довольно просто, как вы только используете scanf(), gets()или fgets(). Я уверен, что у разработчиков Java есть причина для создания класса Scanner, но как это лучше, чем просто иметь обычную функцию для выполнения работы?

Я нашел эту ссылку, которая, кажется, задает тот же вопрос, но ответы только о

«Вам нужно создать объект, потому что он не статичен» ...

Я предполагаю: поскольку Java является объектно-ориентированной, они решили поместить все методы ввода в класс. Они не делали статических методов, так что вы можете иметь все виды различных источников (ввод с клавиатуры, ввод файла ...) в разных объектах?

Буду признателен, если кто-нибудь сможет отредактировать вопрос, чтобы он звучал более четко!

Паблито
источник

Ответы:

34

Ответ «потому что у сканера есть состояние».

Глядя на код для java.util.Scanner , вы увидите ряд закрытых полей, таких как буфер и связанная с ним информация, Matcher, Pattern, источник ввода, информацию о том, закрыт источник или нет, тип из последних найденных совпадений, информация о том, было ли последнее совпадение действительным или нет, основание, используемое для чисел, локаль (информация о том, используете ли вы .или ,как разделитель тысяч), и собственный кэш LRU для недавно использованных шаблонов информация о последнем обнаруженном исключении, некоторая информация о разборе чисел, некоторая информация о разборе логических значений, немного больше информации о разборе целых чисел ... и я думаю, вот и все.

Как видите, это довольно большой блок текста. Это состояние сканера. Чтобы превратить сканер в статический класс, это состояние должно быть сохранено где-то еще. Способ C на самом деле не имеет такого большого состояния. Вы получили fscanf. ФАЙЛ поддерживает некоторое состояние относительно позиции, в которой он находится (но это необходимо передать для каждого вызова fscanf). Если произошла ошибка, вы должны обработать ее (и затем вы начинаете писать код, который выглядит следующим образом ) - и это не говорит вам о такой информации, как «Я ожидал целое число, но нашел строку».

Если взглянуть на теоретически статический сканер - все состояние поддерживается вне класса, оно не инкапсулировано в классе. Другие биты кода могут повозиться с этими переменными. Когда другой код может повлиять на состояние класса, становится очень трудно рассуждать о том, что класс будет делать в любой конкретной ситуации.

Возможно, вы могли бы написать что-то вроде ScannerState { Locale loc; ... }и иметь код, который приводит к:

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

Но тогда это гораздо более громоздко, чем вначале инкапсулировать состояние в объекте Scanner (и не нужно переходить в это состояние).

Наконец, Сканер реализует интерфейс, Iterator<String>который означает, что его можно использовать в коде, таком как:

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

Не имея возможности получить экземпляр класса Scanner, этот тип структуры становится более громоздким в объектно-ориентированном языке.

Сообщество
источник
1
Все, что вы написали, абсолютно верно, хотя InputStream также имеет состояние, а не только Scanner. Если ввод поступает из консоли, как в C, вам не нужно передавать какие-либо параметры, чтобы начать принимать ввод. Я предполагаю , что это было сделано таким образом , чтобы быть последовательным с тем, как другие потоки , которые сделали бы требуют государства.
Нил
@Neil InputStream соответствует FILE*(состоянию позиции) в C.
странный урод
1
Сканер реализует Iterator- нет Iterable. Невозможно использовать сканер в расширенном цикле.
turbanoff
@ratchetfreak Точно. Это то, что должно иметь «состояние» FileInputStreams, но он не применяется для ввода с консоли, так как он уже технически открыт.
Нил
1
@turbanoff Спасибо, что позвонили мне по этому поводу. Я исправил это.
7

краткий ответ: нет. Вы можете получить пользовательский ввод без использования экземпляра сканера.
Например: https://docs.oracle.com/javase/tutorial/essential/io/cl.html или
http://alvinalexander.com/blog/post/java/java-source-code-read-command-line -input

jwenting
источник
String orgName = (new BufferedReader(new InputStreamReader(System.in))).readLine();Это ужасно запутанно по сравнению с использованием а, Scannerа также создает новые экземпляры не одного, а двух объектов, чтобы сразу их отбросить.
Филипп
2
@ Филипп, 1) это громоздко, но это, безусловно, альтернатива, и 2) если вы сразу отбрасываете экземпляры, вы делаете что-то не так (или вам действительно нужно прочитать только одну строку из консоли).
Артуро Торрес Санчес
Вам не нужен сканер для чтения ввода. Вам также не нужен InputStreamReader и вам не нужен BufferedReader. Вы можете работать с «сырым» потоком в System.in, как и в C. Сканер - это просто очень удобный способ потребления этого потока.
Traubenfuchs