Tại sao chúng ta cần một cá thể của Lớp Máy quét để có Đầu vào trên Java?


10

Java là hướng đối tượng, nhưng, tại sao chúng ta cần tạo một đối tượng từ Lớp Máy quét để nhận đầu vào? next()Chẳng hạn, các phương thức không thể là Tĩnh?

C trông tôi khá đơn giản khi bạn chỉ sử dụng scanf(), gets()hoặc fgets(). Tôi chắc chắn có lý do cho các nhà phát triển Java để tạo lớp Máy quét, nhưng làm thế nào tốt hơn là chỉ có một chức năng bình thường để thực hiện công việc?

Tôi đã tìm thấy liên kết này có vẻ như hỏi cùng một câu hỏi, nhưng câu trả lời chỉ là về

"Bạn cần tạo một đối tượng vì không tĩnh" ...

Tôi đoán là: vì Java là hướng đối tượng, họ đã quyết định đặt tất cả các phương thức nhập vào một lớp. Họ không thực hiện các phương thức tĩnh để bạn có thể có tất cả các loại nguồn khác nhau (Nhập bàn phím, Nhập tệp ...) trong các đối tượng khác nhau?

Tôi sẽ đánh giá cao nếu ai đó có thể chỉnh sửa câu hỏi để làm cho nó rõ ràng hơn!

Câu trả lời:


34

Câu trả lời là "bởi vì một máy quét có trạng thái."

Nhìn vào mã cho java.util.Scanner , bạn sẽ thấy một số trường riêng như bộ đệm và thông tin liên quan của nó, Trình so khớp, Mẫu, nguồn đầu vào, thông tin về việc nguồn có bị đóng hay không, loại về điều cuối cùng được khớp, thông tin về việc liệu cuối cùng có khớp hợp lệ hay không, cơ số được sử dụng cho các số, miền địa phương (thông tin về việc bạn đang sử dụng .hoặc ,như một dấu tách hàng nghìn) và bộ đệm LRU của chính nó cho các mẫu được sử dụng gần đây , thông tin về ngoại lệ cuối cùng gặp phải, một số thông tin về phân tích số, một số thông tin về phân tích cú pháp booleans, thêm một chút thông tin về phân tích số nguyên ... và tôi nghĩ đó là về nó.

Như bạn có thể thấy, đó là một khối văn bản khá lớn ở đó. Đó là trạng thái của Máy quét. Để biến Máy quét thành một lớp tĩnh, trạng thái đó sẽ cần được lưu trữ ở một nơi khác. Cách làm C thực sự không có nhiều trạng thái với nó. Bạn đã có một fscanf. FILE duy trì một số trạng thái về vị trí của nó (nhưng cần phải được thông qua cho mỗi lần gọi fscanf). Nếu có lỗi, bạn phải xử lý nó (và sau đó bạn bắt đầu viết mã trông như thế này ) - và điều đó không cho bạn biết thông tin như "Tôi đang mong đợi một Integer, nhưng đã tìm thấy String."

Khi một người nhìn vào Máy quét tĩnh về mặt lý thuyết - tất cả trạng thái được duy trì bên ngoài lớp, nó không được gói gọn trong lớp. Các bit khác của mã có thể tinker với các biến đó. Khi các mã khác có thể sửa đổi trạng thái của lớp, sẽ rất khó để suy luận về những gì lớp sẽ làm trong bất kỳ tình huống nào.

Bạn có thể, có thể, viết một cái gì đó giống như ScannerState { Locale loc; ... }và có mã dẫn đến:

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

Nhưng sau đó, điều này cồng kềnh hơn nhiều so với việc đóng gói trạng thái trong một đối tượng Máy quét ở vị trí đầu tiên (và không cần phải vượt qua trong trạng thái).

Cuối cùng, Máy quét thực hiện giao diện Iterator<String>có nghĩa là người ta có thể sử dụng nó trong mã như:

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

Không thể có được một thể hiện của lớp Scanner, kiểu cấu trúc này trở nên cồng kềnh hơn trong một ngôn ngữ hướng đối tượng.


1
Tất cả mọi thứ bạn đã viết là hoàn toàn đúng, mặc dù InputStream cũng có trạng thái, không chỉ Máy quét. Nếu đầu vào đến từ bảng điều khiển, như trong C, bạn không cần truyền bất kỳ tham số nào để bắt đầu nhập. Tôi cho rằng nó đã được thực hiện theo cách này để phù hợp với những con suối cách khác được thực hiện mà không đòi hỏi nhà nước.
Neil

@Neil InputStream tương đương với FILE*(trạng thái vị trí) trong C.
ratchet freak

1
Máy quét thực hiện Iterator- không Iterable. Không thể sử dụng Máy quét trong vòng lặp nâng cao.
turbanoff

@ratchetfreak Chính xác. Đó là những gì FileInputStreams "trạng thái" phải có, nhưng nó không áp dụng cho đầu vào từ bàn điều khiển vì nó đã được mở về mặt kỹ thuật.
Neil

1
@turbanoff Cảm ơn bạn đã gọi cho tôi về điều đó. Tôi đã sửa nó.

7

Câu trả lời ngắn gọn: bạn không. Bạn có thể nhận đầu vào của người dùng mà không cần sử dụng phiên bản Máy quét.
Ví dụ: https://docs.oracle.com/javase/tutorial/essential/io/cl.html hoặc
http://alvinalexander.com/blog/post/java/java-source-code-read-command-line -đầu vào


String orgName = (new BufferedReader(new InputStreamReader(System.in))).readLine();Điều đó cực kỳ tồi tệ so với việc sử dụng a Scannervà cũng tạo ra các trường hợp mới không chỉ một mà hai đối tượng chỉ để loại bỏ chúng ngay lập tức.
Philipp

2
@Philipp, 1) Nó cồng kềnh, nhưng chắc chắn là một sự thay thế và 2) nếu bạn loại bỏ các trường hợp ngay lập tức bạn đang làm gì đó sai (hoặc bạn thực sự chỉ cần đọc một dòng từ bảng điều khiển).
Arturo Torres Sánchez

Bạn không cần Scanner để đọc đầu vào. Bạn cũng không cần InputStreamReader và bạn không cần BufferedReader. Bạn có thể làm việc với luồng "thô" tại System.in, giống như trong C. Scanner đơn giản là một cách rất thoải mái để sử dụng luồng đó.
Traubenfuchs
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.