NoSuchElementException infinite loop vs. pmd DD-anomaly

Viewed 51

I'm kinda new to programming and very new to gradle and the pmd-plugin, so please have mercy.

If the user enters a number, scanner.nextLine() will throw a NoSuchElementException in every iteration thus creating an infinite loop.

public class Console {
    public int readInteger(String line) {
        Integer x = null;
        while(x == null) {
            try(Scanner scanner = new Scanner(System.in) {
                System.out.print(line);
                x = scanner.nextInt();
            } catch(InputMismatchException exc) {
                  //error message
            } catch(InvalidStateException exc) {
                  //error message
            } catch(NoSuchElementException exc) {
                 //error message
            }
        }
        return x;    
    }
}

I will appreciate every helping hand.

Edit: Realized, that my problem occurs in conjuction with the Scanner.nextLine() method. The order doesn't matter, my loop is still an infinite loop with the same NoSuchElementException.

2 Answers

Just change the

x = scanner.nextLine();

to

x = scanner.nextInt();

Also no it will not enter infinite loop because you use wrong method it simply will not work .

Here's the complete (compilable) example:

import java.util.Scanner;
import java.util.InputMismatchException;
import java.util.NoSuchElementException;

public class Console {
    public int readInteger(String line) {
        Integer x = null;
        while(x == null) {
            try(Scanner scanner = new Scanner(System.in)) {
                System.out.print(line);
                x = scanner.nextInt();
            } catch(InputMismatchException exc) {
                  //error message
            } catch(IllegalStateException exc) {
                  //error message
            } catch(NoSuchElementException exc) {
                 //error message
            }
        }
        return x;
    }

    public static void main(String[] args) {
        Console c = new Console();
        int age = c.readInteger("How old are you? ");
        System.out.printf("You are %d years old.%n", age);
    }
}

Note: InvalidStateException doesn't exist, it is IllegalStateException.

Save this code snippet in Console.java and run it with java 11+ like java Console.java.

If you are entering eg. 42 it works. The "endless loop" starts, if you don't enter an integer, e.g. very old. Now we need to actually handle the exceptions. In that case, InputMismatchException will be thrown. But the wrong input is not consumed and is still in the scanner - so trying again with nextInt() will throw the same exception again. We'll have to read the wrong token first before the user can enter new data. We can read the data with nextLine(), but therefore we need to have access to the scanner, so we need to open the scanner instance earlier - and have the loop and error handling inside the outer try-with-resources.

If the user closes the input stream (with Ctlr+Z under Windows or Cltr+D under Linux), then NoSuchElementException will be thrown, so we'll need to handle that case as well. IllegalStateException will be thrown, if the Scanner instance itself is closed.

Here's a fixed complete example:

import java.util.Scanner;
import java.util.InputMismatchException;
import java.util.NoSuchElementException;

public class Console {
    public int readInteger(String line) {
        Integer x = null;
        try (Scanner scanner = new Scanner(System.in)) {
            while(x == null) {
                try {
                    System.out.print(line);
                    x = scanner.nextInt();
                } catch(InputMismatchException exc) {
                    String wrongInput = scanner.nextLine();
                    System.out.printf("The input '%s' is not a number. Please try again.%n", wrongInput);
                } catch(NoSuchElementException exc) {
                   // no input provided
                   System.exit(1);
                }
            }
        }
        return x;
    }

    public static void main(String[] args) {
        Console c = new Console();
        int age = c.readInteger("How old are you? ");
        System.out.printf("You are %d years old.%n", age);
    }
}
Related