An infinite loop owing to recursion (introductory) - java

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.... ;))

Related

User input and Exception in Java

I want to add an integer to a list based on user input. The user has to type all the integers he/she wishes then press enter. if they finish inputting integer, they are supposed to press the "enter" button without typing anything.
I have made my code, but there are several mistakes
the exception keeps popping up because every time say for example I enter integer 10, then I finish. I press "enter" with nothing. this raises the exception. how do I tackle this problem?
and another thing, how do I make the program so that if the user puts invalid input, instead of crashing or breaking. It asks the user again to prompt the correct input.
this is what I have done
package basic.functions;
import java.util.*;
import java.text.DecimalFormat;
public class Percent {
public static void main(String[] args) {
Scanner reader = new Scanner(System.in);
reader.useDelimiter(System.getProperty("line.separator"));
List<Integer> list = new ArrayList<>();
System.out.println("Enter Integer: ");
while (true) {
try {
int n = reader.nextInt();
list.add(Integer.valueOf(n));
} catch (InputMismatchException exception) {
System.out.println("Not an integer, please try again");
break;
}
}
reader.close();
}
}
output
Enter Integer:
10
Not an integer, please try again
[10]
I'd suggest you utilise Scanner#hasNextInt to identify whether an integer has been entered or not. As for when the "user presses enter without typing anything", we can simply use the String#isEmpty method.
while (true) {
if(reader.hasNextInt()) list.add(reader.nextInt());
else if(reader.hasNext() && reader.next().isEmpty()) break;
else System.out.println("please enter an integer value");
}
note - in this case, you don't need to catch InputMismatchException because it won't be thrown.
while (true) is generally a bad sign, if you ever have that in your code you are almost certainly wrong.
What you probably want is something like this:
String input;
do {
input = reader.next();
// Parse the input to an integer using Integer.valueOf()
// Add it to the list if it succeeds
// You will need your try/catch etc here
while (!input.isEmpty());
Here the loop is checking the exit condition and running until it meets it. Your processing is still done inside the loop as normal but the program flow is a lot cleaner.

How to find out which variable is throwing an exception?

I'm writing a program that culculates tip and total from bill and tip rate.
public void takeUserInput() {
Scanner sc = new Scanner(System.in);
double billAmount;
int tipRate;
try {
System.out.print("What is the bill? ");
billAmount = sc.nextDouble();
System.out.print("What is the tip percentage? ");
tipRate = sc.nextInt();
tc.calculate(billAmount, tipRate);
} catch (InputMismatchException e1) {
String errorMessage = "Please enter a valid number for the ";
// errorMessage += billAmount or
// errorMessage += tipRate ?
}
I'm looking for a way to find out which variable throws InputMismatchException, so I can add which variable name into variable errorMessage and print to the screen.
There are various simple ways to get there:
Call hasNextXxx() prior calling nextXxx().
If you go for one try/catch block per input, it is very clear within your catch block which variable caused the problem (you could then call a generic method with a specific error message to avoid code duplication)
You could use reference types for your variables; if you use Double / Integer instead of double / int ... you could check which of the two variables is still null
You put in a little boolean variable, like billAmountIsValid. Initially that variable is false, you turn it to true after the call to nextDouble(). Then you can easily check in your try block whether you got a valid billAmount.
After some more thinking: you really want a combination of 1 + 2: you see; when the users enters a correct billAmount; why would you want to forget about that value when the second value gives a bad second value? No - you should be looping for each variable, until you receive a valid input. And only then you start asking for the next value!
The variable isn't throwing the exception, the evaluation of the right hand side of the variable assignment is, and so there is no information in the exception to say which variable it was about to assign that to had it succeeded.
What you could consider instead is a new method that encompasses the prompting messages and retries:
billAmount = doubleFromUser(sc, "What is the bill? ", "bill");
Where doubleFromUser is:
static double doubleFromUser(Scanner sc, String prompt, String description){
while(true) { //until there is a successful input
try {
System.out.print(prompt); //move to before the loop if you do not want this repeated
return sc.nextDouble();
} catch (InputMismatchException e1) {
System.out.println("Please enter a valid number for the " + description);
}
}
}
You will need a different one for int and double, but if you have more prompts, you will save in the long run.

How to ask te user and depending on his response run all the code again?

I'm a newbie in Java. I started these days and I'm practicing the catch and try exception. I have this code below which solve an operation between to numbers and I'd like to know what can I do, if for example I want that the user, once he makes an operation and get his result, that this has the possibility to make another operation. something like comes up a question asking if he wants to realize another problem and the code run again from the beginning.
package justpractice;
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner Operation = new Scanner(System.in);
int x=1;
while(x==1){
try{
System.out.println("Insert numerator");
int n1 = Operation.nextInt();
System.out.println("Insert denominator");
int n2=Operation.nextInt();
double division = n1/n2;
System.out.println(division);
x=2;
}
catch(Exception e){
System.out.println("Insert a valid value");
}
}
}
}
You can do, for example, adding an if statement with a
System.out.println("Do you want to recalculate ? (1/0 Yes/No)");
Operation.nextInt();
then if the input is 1, keep x = 1, else do x = 2.
Try this code amendment;
package justpractice;
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner Operation = new Scanner(System.in);
//better practice
while(true){
try{
System.out.println("Insert numerator");
int n1 = Operation.nextInt();
System.out.println("Insert denominator");
int n2=Operation.nextInt();
double division = n1/n2;
System.out.println(division);
System.out.println("Continue? (y/n)");
String response = Operation.nextLine();
if (response.equals("n")){
break;
}
}
catch(Exception e){
System.out.println("Insert a valid value");
}
}
}
}
To allow your user to calculate division again, you could use a do-while loop. With this loop you can execute the code inside once, ask the user if they would like to calculate again, and repeat the code if they do.
An outline of your code with the loop would something like this:
...
boolean again = true;
do { //Main loop
try {
... //Your division code
... //Put code to ask the user if they want to calculate again here
} catch(Exception e) {
...
}
} while(again == true); //Repeat if the user wants to continue
To get input on if the user wants to calculate again, I recommend using another do-while loop with your Scanner. This loop will allow you to repeat the code when the answer is invalid. In this case, when it's not "y" or "n".
String input;
do {
System.out.println("Would you like to continue? (y/n)");
input = operation.next();
} while(!input.equalsIgnoreCase("y") && !input.equalsIgnoreCase("n"));
After you have got the user's input, you still need to terminate the loop if they said "n". To do this you could use an if statement.
if(input.equalsIgnoreCase("n")) {
again = false; //Terminate the main loop
operation.close(); //Prevent any resource leaks with your Scanner
}
There is no need to check if the user input "y" as again is set to true by default.
Side Note: Variables should always be camelCase. Look at the Java Naming Conventions to learn more about naming things in Java.
EDIT:
The reason the console is repeatedly logging that you entered a non-number even though you entered it once, I'm not exactly sure. I think it's because the call to nextInt() never finishes because of the InputMismatchException being thrown, causing the next call to nextInt() (After the do-while repeats) to think that the letter/symbol you just entered is the one you want to process, repeating the exception over and over again.
To solve this, add this line into your catch block:
if(operation.hasNext()) operation.next();
This will call next() and complete the process of marking the letter/symbol you just entered as already processed, then repeat the do-while loop as normal.

Infinitive recursion when catching an Exception

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();
}
}
}

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
}

Categories