Reputation: 103
I was trying to write a method that enables one to input integers via Scanner without the program crashing due to exceptions. Here's what I initially had:(here, sc is a Scanner object)
public static int inputInt(String message) {
int returnval = 0;
System.out.println(message);
try {
returnval = sc.nextInt();
} catch (Exception ex) {
System.out.println("You must enter an integer.");
inputInt(message);
}
return returnval;
}
When I tested the program with an invalid input, an infinite loop was initiated, with the message and "You must enter an integer" being printed many times before being halted by Eclipse. I fixed this using the following code:
public static int inputInt(String message) {
int returnval = 0;
System.out.println(message);
String valstring = sc.nextLine();
try {
returnval = Integer.parseInt(valstring);
} catch (Exception ex) {
System.out.println("You must enter an integer.");
inputInt(message);
}
return returnval;
}
Why does the first method fail, but not the second? Might there be a cleaner way to accomplish this?
Upvotes: 0
Views: 118
Reputation: 719
Scanner.nextInt()
has the capacity of throwing 3 exceptions:
1- InputMismatchException
: Thrown when the next item isn't an int
.
2- NoSuchElementException
: When there isn't a next anything.
3- IllegalStateException
: If the Scanner
has been closed.
So when you enter an illegal value, such as "abc" for example, the top piece of code does this:
Takes "abc" and tries to convert it to an int. (nextInt()
). Because "abc" isn't a number, it cannot convert, and throws an InputMismatchException
. However, beacuse the method didn't complete successfully, "abc" is left as the next item to be read. Because the exception was thrown, the code inside your catch
block is run. It prints out "You must enter an integer." Then it calls itself again, passing itself message
. It then initialises returnval
and prints out the same message
from the first run, because you passed it to yourself again. Then you enter the try
block, where, because "abc" wasn't read successfully and left there, the scanner reads "abc" and tries to convert it to an int
again. Of course, this won't work, and the cycle starts again.
Now you know what your problem is, you can figure out a solution. The most elegant in my mind would be to use the Scanner
method, hasNextInt()
. This will return a boolean
letting you know if the next item to be read can be converted to an int
. For example, if the next item is "abc", the method will return false
. For the next item "1", the method will return true
. So, if you modify your code like so, it should work:
public static int inputInt(String message) {
int returnval = 0;
System.out.println(message);
while(!sc.hasNextInt()) {
sc.nextLine();
System.out.println("You must enter an integer.");
}
returnval = sc.nextInt();
return returnval;
}
What this code does:
1- Initializes returnval
and prints out message
.
2- Enters the while loop as long as the scanner doesn't have an int
to read. Effectively "waiting" for the next int
to be input.
3- Once it has an int
to read, it reads the int and saves the value in returnval
.
4- It returns returnval
to the caller.
(Slow and steady wins the race, lol. I always seem to be the slow one to answer. Maybe because I write small novels as answers.... ;))
Upvotes: 1
Reputation: 59273
Yes, there is a cleaner way: use hasNextInt
:
public static int inputInt(String message) {
System.out.println(message);
while (!sc.hasNextInt()) {
sc.nextLine(); // clear the bad input first
System.out.println("You must enter an integer.");
System.out.println(message); // why use recursion? just use a while loop
}
return sc.nextInt();
}
The changes I made:
hasNextInt
, so you don't have to use exceptionssc.nextLine();
(the root of your problem)Upvotes: 2