JEP 477 [1] (Implicitly Declared Classes and Instance Main Methods) includes some new APIs for simple text-based interaction. This issue covers the implementation of those APIs.
A new public class java.io.IO is introduced, having the following three methods:
1. public static void println(Object obj)
2. public static void print(Object obj)
3. public static String readln(String prompt)
These methods are simply static adapter methods that invoke the corresponding instance methods on the result of System.console(). If System.console() returns null, these methods should throw IOError.
Instance methods with these names should be added to java.io.Console.
All of these new APIs (both the new methods on Console and the IO class) should be marked as Preview.
Some rationale follows.
IO is automatically static-imported into implicit classes, as described by the JEP. As such, these are convenience methods that shouldn't do anything that isn't possible on Console itself. While they could be entirely implemented in terms of existing Console methods (e.g., printf and readLine) these methods have their own purpose and it makes sense to have them directly as instance methods on Console as well.
There is no need for the primitive overloads of print and println that are present on PrintStream and PrintWriter. First, we have autoboxing, so overloads aren't necessary to remove source code clutter. Second, the overhead of boxing is inconsequential compared to the overhead of performing console I/O, so it's not worth adding API clutter in an attempt to improve performance.
Console already has readLine() method; why have readln()? The goal is to have a method that prints a prompt and reads a line. The readLine() methods mostly fit this bill. However, the no-arg version of readLine() reads a line *without* issuing a prompt, which could be confusing for beginners. The other overload readLine(fmt, args...) takes a *format string* followed by optional arguments. We want the prompt to be a plain string, not a format string. If a readLine(String plainString) overload were added, it would mean that additional arguments would change the semantics of the first argument. This is bad overload API style and should be avoided.
The name "readln" was chosen over the alternative "input" because it's a better verb, its naming is evocative of "println" which is already used elsewhere in the JDK, and "readln" is something of an homage to Pascal.
These methods are specified in terms of Console for the following reasons. First, the goal here is to introduce "convenience methods" that are entirely implemented in terms of other mechanisms. These methods shouldn't introduce any new logic or abstractions. They should be thin layer over existing APIs.
Second, these methods need to be specified in terms of *something*; the choices are System.in plus System.out or Console. The behaviors cannot be left unspecified (either explictly or implicitly), because operations that include buffering and flushing are intrusive with respect to the underlying mechanism in use. If, for example, readln() were implemented in terms of System.in, by wrapping it in a BufferedReader, then direct use of System.in would have unpredictable behavior because of the buffering.
Third, Console was chosen because it's an API that fuses input and output. The readln(prompt) method needs to emit the prompt, flush the output, and then read input. In addition, the Console abstraction allows an enhanced input experience such as line editing and history. If these methods were defined in terms of System.in+System.out, then readln(prompt) would need to be specified to have various side effects on System.out as well as System.in. There are also a variety of decisions made in Console that would need to be reimplemented or respecified if these methods were based on System.in+System.out.
Some additional cleanups to Console are necessary though. For example, the behavior of Console when stdin or stdout is redirected should be specified. The circumstances under which System.console() returns null (and these methods throw IOError) should also be narrowed. For example, one case where this might be reasonable is if System.in or System.out are closed. Finally, Console should be specified to be an abstraction around System.in and System.out, and not something like /dev/tty or CON: on Windows. While an interactive Console might imply that the implementation should go directly to /dev/tty, this is rare and unusual behavior for programs. Historically Console has not done this and has attempted to use System.in and System.out. Finally, we do want to support redirection, which is not available if something like /dev/tty or CON: were used.
[1]: https://openjdk.org/jeps/477
A new public class java.io.IO is introduced, having the following three methods:
1. public static void println(Object obj)
2. public static void print(Object obj)
3. public static String readln(String prompt)
These methods are simply static adapter methods that invoke the corresponding instance methods on the result of System.console(). If System.console() returns null, these methods should throw IOError.
Instance methods with these names should be added to java.io.Console.
All of these new APIs (both the new methods on Console and the IO class) should be marked as Preview.
Some rationale follows.
IO is automatically static-imported into implicit classes, as described by the JEP. As such, these are convenience methods that shouldn't do anything that isn't possible on Console itself. While they could be entirely implemented in terms of existing Console methods (e.g., printf and readLine) these methods have their own purpose and it makes sense to have them directly as instance methods on Console as well.
There is no need for the primitive overloads of print and println that are present on PrintStream and PrintWriter. First, we have autoboxing, so overloads aren't necessary to remove source code clutter. Second, the overhead of boxing is inconsequential compared to the overhead of performing console I/O, so it's not worth adding API clutter in an attempt to improve performance.
Console already has readLine() method; why have readln()? The goal is to have a method that prints a prompt and reads a line. The readLine() methods mostly fit this bill. However, the no-arg version of readLine() reads a line *without* issuing a prompt, which could be confusing for beginners. The other overload readLine(fmt, args...) takes a *format string* followed by optional arguments. We want the prompt to be a plain string, not a format string. If a readLine(String plainString) overload were added, it would mean that additional arguments would change the semantics of the first argument. This is bad overload API style and should be avoided.
The name "readln" was chosen over the alternative "input" because it's a better verb, its naming is evocative of "println" which is already used elsewhere in the JDK, and "readln" is something of an homage to Pascal.
These methods are specified in terms of Console for the following reasons. First, the goal here is to introduce "convenience methods" that are entirely implemented in terms of other mechanisms. These methods shouldn't introduce any new logic or abstractions. They should be thin layer over existing APIs.
Second, these methods need to be specified in terms of *something*; the choices are System.in plus System.out or Console. The behaviors cannot be left unspecified (either explictly or implicitly), because operations that include buffering and flushing are intrusive with respect to the underlying mechanism in use. If, for example, readln() were implemented in terms of System.in, by wrapping it in a BufferedReader, then direct use of System.in would have unpredictable behavior because of the buffering.
Third, Console was chosen because it's an API that fuses input and output. The readln(prompt) method needs to emit the prompt, flush the output, and then read input. In addition, the Console abstraction allows an enhanced input experience such as line editing and history. If these methods were defined in terms of System.in+System.out, then readln(prompt) would need to be specified to have various side effects on System.out as well as System.in. There are also a variety of decisions made in Console that would need to be reimplemented or respecified if these methods were based on System.in+System.out.
Some additional cleanups to Console are necessary though. For example, the behavior of Console when stdin or stdout is redirected should be specified. The circumstances under which System.console() returns null (and these methods throw IOError) should also be narrowed. For example, one case where this might be reasonable is if System.in or System.out are closed. Finally, Console should be specified to be an abstraction around System.in and System.out, and not something like /dev/tty or CON: on Windows. While an interactive Console might imply that the implementation should go directly to /dev/tty, this is rare and unusual behavior for programs. Historically Console has not done this and has attempted to use System.in and System.out. Finally, we do want to support redirection, which is not available if something like /dev/tty or CON: were used.
[1]: https://openjdk.org/jeps/477
- csr for
-
JDK-8331610 Implement java.io.IO
- Closed
- relates to
-
JDK-8333358 java/io/IO/IO.java test fails intermittently
- Resolved
-
JDK-8332922 Test java/io/IO/IO.java fails when /usr/bin/expect not exist
- Resolved
-
JDK-8323335 JEP 477: Implicitly Declared Classes and Instance Main Methods (Third Preview)
- Closed
(1 links to)