Ayesha
Ayesha

Reputation: 103

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?

Upvotes: 0

Views: 118

Answers (2)

Andrew Gies
Andrew Gies

Reputation: 719

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

Upvotes: 1

tckmn
tckmn

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:

  • 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)

Upvotes: 2

Related Questions