Infinitive recursion when catching an Exception - java

Here is my code for inputting a student number:
When the user inputs the number in a unexpected format I will ask them to reinput by recursion. But it ends up with an infinitive recursion. Why?
private static int inputStudentNumber(){
System.out.println("Enter the student number:");
int studentNum;
try {
//Scanner in initialized before calling this method
studentNum = in.nextInt();
return studentNum;
} catch(Exception e) {
System.out.println("Invalid input, it can only be integer.");
return inputStudentNumber();
}
}

Take a closer look at the javadocs for Scanner.nextInt:
This method will throw InputMismatchException if the next token cannot be translated into a valid int value as described below. If the translation is successful, the scanner advances past the input that matched. (emphasis added)
If it's not successful, the scanner isn't advanced. That means that if you try to invoke nextInt() again, you'll be trying to get an int from the same token as before, and you'll once again get an InputMismatchException.
Your code basically says: try to read the next token as an int. If that fails, recurse to try to read the token as an int again. If that fails, recurse to try to read the token as an int again. If that fails... (and so on, until you get a StackOverflowException from too much recursion).
If you want to use recursion for this, you should probably use next() to skip to the next token. And only catch InputMismatchException, so that you won't also catch NoSuchElementException (which won't happen for System.in, but is good practice in general -- what if you later decide to read from a file, and that file has reached its end?).
} catch(InputMismatchException e) {
System.out.println("Invalid input, it can only be integer.");
in.next(); // skip this token
return inputStudentNumber();
}
An even better approach would be to avoid using the exception to control your logic in the first place. To do this, you'd have to know ahead of time whether nextInt will succeed. Luckily for you, hasNextInt() lets you do exactly that!
private static int inputStudentNumber() {
System.out.println("Enter the student number:");
if (in.hasNextInt()) {
return in.nextInt();
} else {
System.out.println("Invalid input, it can only be integer.");
in.next(); // consume the token
return inputStudentNumber();
}
}
The advantage here -- besides the general "don't use exceptions for control flow" advice -- is that the base case is super clear. If there's an int ready, that's your base case; if not, you have to advance the scanner and try again.

The problem is that if a non-integer is entered as input, then that input is not consumed by the scanner. So you just keep reading it.
You may want to just read the input as a string and then try to convert it separately.

Your problem is probably that in.nextInt() is throwing an Exception. A code smell that I see is that you use:
catch(Exception e) {
....
}
The best practice here is to only catch the specific Exception you are expecting, so it should be:
catch(InputMismatchException e) {
....
}
If you do this, then whatever in.nextInt() is throwing will properly propagate to the top, and you will probably see that in is not initialized or some such problem.
See here for the Exceptions that nextInt() can throw.
http://docs.oracle.com/javase/7/docs/api/java/util/Scanner.html#nextInt()

try this...
private static int inputStudentNumber(){
System.out.println("Enter the student number:");
int studentNum;
int var = 1;
while(var ==1)ยด
{
try{
studentNum = in.nextInt();
var=0;
return studentNum;
}catch(Exception e){
System.out.println("Invalid input, it can only be integer.");
var=1;
return inputStudentNumber();
}
}
}

Related

Can i use Scanner within a catch statement [duplicate]

This question already has answers here:
try/catch with InputMismatchException creates infinite loop [duplicate]
(7 answers)
Closed 7 years ago.
If I type in letter for the first Scanner statement i get "error!!" and "enter a number" but cannot input another number.I am a beginner and do not know whether an input statement can be used within a catch statement
import java.util.InputMismatchException;
import java.util.Scanner;
public class excep {
public static void main(String args[]){
int n;
Scanner input=new Scanner(System.in);
try{
System.out.println("ENTER A NUMBER: ");
n=input.nextInt();
}
catch(InputMismatchException e){
System.out.println("ERROR!!! \nENTER A NUMBER :");
n=input.nextInt();
}
}
}
You must have to eat or remove characters from the buffer before you restart input. For the sake of simplicity, your code should look alike :
while(true) {
Scanner input=new Scanner(System.in);
try {
System.out.println("ENTER A NUMBER: ");
n=input.nextInt();
break;
}catch(InputMismatchException e) {
System.out.println("ERROR!!! \nENTER A NUMBER :");
input.next(); // eat some chars
}
}
The biggest problem with your approach is that if nextInt() fails because the user did not enter a valid integer, the scanner does not advance. The scanner has a pointer that points to the next character to read from the input. When you use nextInt(), if the next "token" is an integer, the scanner will advance the pointer past the integer. But if it isn't an integer, an exception is thrown--and the pointer stays in the same place as before. So when you catch the exception, then call nextInt(), the scanner tries to read the same invalid integer that it just tried to read last time.
To skip over the bad integer, you can say input.next() to skip over one token, or input.nextLine() to skip over the entire remainder of the input line, before trying to read another integer. (Both of these return String results, but you can discard the result since it's not important to you).
However, this is not really a good way to use try/catch. If the user is having a bad day and enters another invalid integer, the scanner will throw an exception, and it will not be caught, since you are not in the try when the second exception is thrown. catch doesn't loop back and catch it again. The best idiom for this is a loop:
boolean validIntegerEntered = false;
System.out.println("ENTER A NUMBER: ");
while (!validIntegerEntered) {
try {
n=input.nextInt();
validIntegerEntered = true; // will not get here if nextInt()
// throws an exception
}
catch (InputMismatchException e) {
input.nextLine(); // let the scanner skip over the bad input
System.out.println("ERROR!!! \nENTER A NUMBER :");
// don't call nextInt() here; loop back, and nextInt() will be called
// in the try statement
}
}
Syntactically, Yes you can but you shouldn't use it. Why?
input.nextInt(); can throw InputMismatchException or NoSuchElementException or IllegalStateException if the scanner is closed.
If any of those exceptions occur within the catch block, you are not handling them. You should probably look at how to implement a retry catch
You are using the input.nextInt(), means it can only accepts int values.If you want to enter string values then better to use input.nextine().
In your code in catch block you are using again input.nextInt() but scanner has already wrong input in it that why it throw the exception.If you want to take input inside catch block then try the below code:
catch(InputMismatchException e){
System.out.println("ERROR!!! \nENTER A NUMBER :");
Scanner input1=new Scanner(System.in);
n=input1.nextInt();
System.out.println("Inside catch:"+n);
}

Java catch statement in loop does not work

When I run th code, and say input 2.5, the output I get is:
2.5, 4 times "error", and 5.
It means that the computer goes through the catch statement every time, instead of asking for input every time it loops through the while loop and enters the try block.
public static void main(String[] args)
{
Scanner s1 = new Scanner(System.in);
int n = 0;
while(n<5)
{
try
{
int grade = s1.nextInt();
System.out.println(grade);
}
catch(Exception e)
{
System.out.println("error");
}
n++;
}
System.out.println(n);
}
When you enter "2.5", nextInt() consumes the 2. The next thing being scanned by the very same nextInt() will be . and that cannot be successfully scanned by nextInt(), so you get the error. nextInt() can only be used to scan int numbers, if you want to scan fractions like 2.5, you need nextDouble().
By the way, the exception objects hold useful information. If you do this, you're just hiding the error information:
catch (Exception e) {
System.err.println(error):
}
Instead do this:
catch (Exception e) {
e.printStackTrace();
}
And don't mix System.out and System.err. System.out is for the normal program output but not for logging, debug, info, warning, error or such messages. Those should go to System.err.
Basically, there is a "cursor" that points to the next character of your input line that a Scanner will read. If the input line looks like
bad input
the cursor starts before the b. If you call nextInt(), the Scanner will throw an exception, because b can't be the start of an integer. But the cursor stays in the same place. So next time you call nextInt(), the same error will happen all over again.
You need to do something that will "consume" the bad input so that the cursor will move. s1.nextLine(), as in almas' answer, will consume the entire rest of the line.
If the input is
2.5
nextInt() will throw an exception because 2.5 is not an integer. Once again, the cursor stays in the same place, so the error will keep coming up, unless you do something to consume the 2.5. If you want the program to actually read 2.5, then don't use nextInt() since 2.5 is not an integer.
According to the documentation:
This method will throw InputMismatchException if the next token cannot be translated into a valid int value as described below. If the translation is successful, the scanner advances past the input that matched.
This means that if the translation is NOT successful, the scanner does not advance past the input. It keeps trying to translate .5 in every iteration.
You can get the result I assume you expect by adding s1.next() in your catch statement to clear the token in the Scanner:
public static void main(String[] args)
{
Scanner s1 = new Scanner(System.in);
int n = 0;
while(n<5)
{
try
{
int grade = s1.nextInt();
System.out.println(grade);
}
catch(Exception e)
{
**s1.next();**
System.out.println("error");
}
n++;
}
System.out.println(n);
}
And as many have already mentioned, output all possible information from exception!

An infinite loop owing to recursion (introductory)

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?
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:
Uses hasNextInt, so you don't have to use exceptions
Added sc.nextLine(); (the root of your problem)
Instead of recursion, just does a while loop (why recurse when you can simple loop?)
Eliminated temporary variable
Made it more readable (IMO)
Problem
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.
Solution
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.... ;))

scanner.nextInt(), out of range avoidance?

I've finished a simple program that converts a decimal number to binary (32bit). I would like to implement some type of error message should the user enter in an overflow number (anything over 2147483647). I tried a if_else , loop, but quickly found out I couldn't even do that. So I messed with taking the input as a string, and then using some things like .valueOF() etc, and still can't seem to get around to the solution.
I don't see how I can compare any value to a >2147483648 if I can't store the value in the first place.
Here's the bare code I have for the getDecimal() method:
numberIn = scan.nextInt();
Edit:: After trying the try / catch method, running into a compile error of
"non-static method nextInt() cannot be referenced from a static context".
My code is below.
public void getDec()
{
System.out.println("\nPlease enter the number to wish to convert: ");
try{
numberIn = Scanner.nextInt();
}
catch (InputMismatchException e){
System.out.println("Invalid Input for a Decimal Value");
}
}
You can use Scanner.hasNextInt() method, which returns false, if the next token cannot be converted to an int. Then in the else block, you can read the input as string using Scanner.nextLine() and print it with an appropriate error message. Personally, I prefer this method :
if (scanner.hasNextInt()) {
a = scanner.nextInt();
} else {
// Can't read the input as int.
// Read it rather as String, and display the error message
String str = scanner.nextLine();
System.out.println(String.format("Invalid input: %s cannot be converted to an int.", str));
}
Another way to achieve this is of course, using try-catch block. Scanner#nextInt() method throws an InputMismatchException, when it can't convert the given input into an integer. So, you just need to handle InputMismatchException: -
try {
int a = scan.nextInt();
} catch (InputMismatchException e) {
System.out.println("Invalid argument for an int");
}
I suggest you surround that statement with a try/catch block for NumberFormatException.
Like so:
try {
numberIn = Integer.valueOf(scan.next());
}catch(NumberFormatException ex) {
System.out.println("Could not parse integer or integer out of range!");
}
use exceptions.. whenever a number is entered more than its storing capacity then exception will be raised
Refer docs.oracle.com/javase/tutorial/essential/exceptions/
You can user hasNextInt() method to make sure that there is an integer ready to be read.
try this :
long num=(long)scan.nextLong();
if (num > Integer.MAX_VALUE){
print error.
}
else
int x=(int)num;
or try catch:
try{
int number=scan.nextInt()
}
}catch(Exception ex){
print the error
}

Get an input from user scanner class

normally when I get a user interaction from a keyboard I use class scanner to do so but the problem I noticed when using its methods is that it's not handling exception! For example
Scanner input = new scanner();
Int number = input.nextInt();
The above works good for all integer numbers but if the user mistakly entered a character or a string, it will throw an exception and stops executing the rest of the program.
My question is there any way to avoid this?
Thank you in advance.
Try to catch the exception. Or use the hasNextInt method to prevent the exception from being thrown in the first place.
try {
int number = input.nextInt();
} catch (InputMismatchException e) {
System.out.println("That wasn't a number!");
}
Perhaps you should catch the exception and ask the user again?
See http://www.functionx.com/java/Lesson14.htm for exactly your problem.

Categories