If you take a look at documentation of Scanners methods like hasNext(String)
or hasNextDouble
you will see that it
Throws:
IllegalStateException - if this scanner is closed
(emphasis mine)
So to throw IllegalStateException
you first need to close Scanner, not stream from which it is reading data.
So lets take a look at this example:
Scanner sc = new Scanner(System.in);
System.out.println("type something:");
System.out.println(sc.hasNext());// true
System.out.println("your data: "+ sc.nextLine());
sc.close();
System.out.println("type something:");
System.out.println(sc.hasNext());// throws java.lang.IllegalStateException: Scanner closed
Last line throws IllegalStateException
because you are invoking hasNext
method on closed scanner (so we know that after invoking sc.close()
stream from which it reads must be also closed so we can safely assume that there are no more elements to read, or since stream was closed we may not be allowed to read it).
Now if we don't close scanner but close System.in
we will still be able to use this instance of scanner without getting exceptions. So lets simply change sc.close();
to System.in.close()
(I will skip exceptions handling for simplicity):
Scanner sc = new Scanner(System.in);
System.out.println("type something:");
System.out.println(sc.hasNext());// true
System.out.println("your data: "+ sc.nextLine());
System.in.close();
System.out.println("type something:");
System.out.println(sc.hasNext());// false
As you can see there is no exception here because it wasn't scanner which was closed, but stream which scanner which was being read.
Why closing System.in
doesn't cause scanner to throw exception?
I suspect that decision to not throw exception here was made with assumption that exception symbolize problem with code. If programmer allowed scanner to being closed he should also make sure that this particular closed instance of scanner will not be used anywhere.
Now returning false
instead of throwing exception is normal reaction where there is no more elements to read. So if stream which scanner was reading was closed naturally (like when we read text file and read its last line so there is nothing more to read) scanner handles this situation like something normal (so there is no need to point that this is some exceptional situation).
Now in your loop you are kind of combining these two scenarios. Your code can be simplified to something like:
Scanner sc = new Scanner(System.in);
System.out.println("type something:");
System.out.println(sc.hasNext());// true
System.out.println("your data: "+ sc.nextLine());
System.in.close();
sc = new Scanner(System.in);//IMPORTANT
System.out.println("type something:");
System.out.println(sc.hasNext());// false
As you see in line
sc = new Scanner(System.in);//IMPORTANT
you are creating new instance of scanner which wasn't closed yet, so its hasXYZ
methods always returns false
because System.in
can't provide no more values.
Additional trap
One problem which I didn't mentioned earlier is fact that in case of wrong input, which is neither "exit"
nor double
if you are are not consuming that invalid cached value from scanner by using any of nextXZY
methods like hasNext("exit")
or hasNextDouble
will be still based on that invalid data, like:
Scanner sc = new Scanner("foo 1");
System.out.println(sc.hasNextInt());//false because `foo` is not integer
System.out.println(sc.hasNextInt());//also false because we are still
//reading `foo` which is not integer
String str = sc.next();//lets read (sonsume) foo
System.out.println(sc.hasNextInt());//true since 1 is integer
Solution
Simplest solution to such problem is creating only one instance of Scanner
which will handle System.in
and reuse it in your entire application. Then at the end of your application you can decide to close your scanner or System.in
.
So your code can look like:
boolean finished;
Scanner inputScanner = new Scanner(System.in);
do {
finished = inputScanner.hasNext("exit");
boolean validNumber = inputScanner.hasNextDouble();
if (validNumber) {
double number = inputScanner.nextDouble();
System.out.print(number);
} else if (!finished) {
System.out.println("Please try again.");
inputScanner.next();// lets not forget to consume from scanner cached
// invalid data which is neither double or "exit"
}
} while (!finished);
inputScanner.close();