I’m sure some of you have found the need occasionally to use the “console” input/output support provided by the JDK via System.in
and System.out
— if nothing else just to implement an occasional more complicated command-line “scripty” app to be run in a Linux environment and piped input in or out from/to another program.
The problem so far has been the fact that while System.out
is a PrintStream
instance — System.in
is an InputStream
implementation! So while the output deals with characters, the input deals with bytes — and you have to start wrapping it up (through a few layers!) into a BufferedReader
so you can read input line by line.
Luckily, JDK 1.6 introduces a nice function on the System
class : console()
— which does solve these problems.
Even more, the Console
class actually offers a nice printf()
function for the C nostalgics — which, as you would expect, acts exactly like printf()
in C! The only thing that is lacking, if you ask me, is a scanf()
equivalent — that can still be achieved via a Scanner
instance, however, it obviously requires more coding. I’ll come back to the whole scanf()
discussion though, for now let’s have a look at printf()
.
Prior to this, you had a few ways of printing something like “User {username} is {years} old”:
- Call
System.out.print
a few times:
String username = ...; int age = ...; System.print( "User " ); System.print( username ); System.print( " is " ); System.print( age ); System.println( " old" ); |
- Simply use string concatenation in a call to
System.out.println
:
String username = ...; int age = ...; System.out.println( "User " + username + " is " + age + " old" ); |
- Use
String.format()
:
String username = ...; int age = ...; String output = String.format( "User %s is %d old", username, age ); System.out.println( output ); |
With the introduction of the Console
class you can now also mix String.format
and outputting this to the console in one line like this:
String username = ...; int age = ...; System.console().printf( "User %s is %d old", username, age ); |
As to be expected with something like this, there is a readLine()
method, which to be honest saves anyone who’s be dealing with console-based programs the whole spaghetti code of creating a BufferedReader
etc — just so you can read everything the user types till they press ENTER. Even more, you can now show a prompt and read user input in one line — System.console().readLine()
can be passed the same set of parameters as printf()
does: it formats the output, presenting the user with the prompt then waits for user input.
I haven’t personally ever had to use this, but for those of you who need it, there is even a readPassword()
function, which obviously doesn’t display the output 🙂
I’ve put together a (very) short piece of code to exemplify this — see below (also you can download the full source code at the bottom of this post):
if (System.console() == null) { System.out.println("No console!"); System.exit(1); } System.console().printf("The current timestamp is %d\n", System.currentTimeMillis()); String line = System.console().readLine(); System.console().printf("Line read was: %s\n", line); line = System.console().readLine("Username:"); System.console().printf("Your username is '%s'\n", line); line = new String(System.console().readPassword("Password:")); System.console().printf("Your password is '%s'\n", line); |
Now, as I said, one thing that I reckon Oracle needs to add to this is an scanf()
equivalent — this is the kind of code that you have to write to read a String
and an int
:
/* * Scanf-like */ line = System.console().readLine("Username and age:"); Scanner scan = new Scanner(line); // %s%d String name = scan.next(); int age = scan.nextInt(); System.console().printf("%s is %d y/o\n", name, age); |
Not ideal, huh? Imagine how cool it would be if you could write this though:
StringBuilder name = new StringBuilder(); MutableInt age = new MutableAge(); System.console.scanf( "Username and age:", "%s%d", name, age ); |
Note that I’m suggesting using the likes of StringBuilder
and MutableInt
(you will need Apache Commons Lang for this) — since String
is not mutable, and the parameters passing in Java is by value (so passing an int
means we pass a copy of the value, not a reference to the original int variable, so we can’t modify that!). This, I think, would help everyone who does any Java/Groovy system scripting — as it saves a bit of coding around the whole Scanner
interface, as you can see above.
One last thing about Console
— and it’s a sad one to be honest: you might have noticed in the above code the following construct:
if (System.console() == null) { System.out.println("No console!"); System.exit(1); } |
That is because according to the System.console()
JavaDoc: Returns the unique Console object associated with the current Java virtual machine, if any. So there is no guarantee that there will be a Console
instance in the system 🙁 You could think “ok, fair enough, this makes sense for server apps which run in a nohup environment and log everything etc”, and in those circumstances I would guess it’s acceptable not to have a Console
instance. What I don’t see as acceptable, is the current set of IDE’s (Eclipse included — as much as I rate it) all return a null for System.console()
! That, annoyingly, makes the testing of any console-based Java program awkward in Eclipse: rather than simply starting the code in debug, you have to put together a bit command-line, execute it in terminal, attach Eclipse to the process… and so on — eeeewwww! So Eclipse guys, if you’re reading this, please give us a Console
implementation under JDK 1.6!
Attached, the code for the short test program mentioned above: ConsoleTest.java.bz2
As per usual, would love to hear some comments — or drop me a line — in fact would love to hear when there is an IDE there which provides a Console
implementation. And more to the point, would love to find out when Eclipse does that too!