0

I have a problem with Scanner in a while loop.

Whenever I have a while (true) loop reading from scanner, as soon as I close scanner object, the while loop starts to iterate eternally thinking I closed stream. But I open and close stream in a separate method, that as I imagine should be opening and closing it each time the method is called, but it doesn’t. What currently helps is creating Scanner object outside the loop and sending it as a parameter to the method, plus adding scannerObject.next() method.

I don’t want to change loop conditions. Are there normal ways to avoid this error?

import java.util.Scanner;

public class Main {

    public static void main(String[] args) {
        //testing how to have a while loop that only stops if q is entered
        Scanner userInput = new Scanner(System.in);
        
        while(true) {               
            int number = input(userInput);
            
            switch(number) {
                
            case 0: // returns 0 for Invalid input
                System.out.println("Invalid input. Enter an integer or q to Quit");
                break;
                
            case -1: // returns 1 to quit the program
                //
                System.out.println("You chose to quit. Good-bye");
                System.exit(0);
                break;
                
            default: // default is done when the input returns an integer, so valid input
                System.out.println("You entered " + number);
                break;
            }                   
            //System.out.println("I am now after the while loop");
        }// end of while(true) loop for userinput
        
    }

    private static int input(Scanner userInput) {
        System.out.println("Enter an integer or q to Quit the program: ");                  
        int result = 0;         
        if(userInput.hasNextInt()) {
            result = userInput.nextInt();               
        }           
        else {              
            if(userInput.hasNext("q")) {                    
                result = -1;
            }
            else {
                
                userInput.next();
//              result = 0;
            }
        }           
        return result;          
    }
}
rainbow
  • 3
  • 2
  • "_...as soon as I close scanner object..._" - Don't close a `Scanner` wrapping `System.in`, as it will [close the underlying stream aswell](https://stackoverflow.com/questions/14142853/close-a-scanner-linked-to-system-in). – maloomeister May 16 '23 at 07:00
  • Does this answer your question? [Close a Scanner linked to System.in](https://stackoverflow.com/questions/14142853/close-a-scanner-linked-to-system-in) – maloomeister May 16 '23 at 07:01
  • This code sample seems irrelevant to your Question. You are not closing your Scanner anywhere in that code. – Basil Bourque May 16 '23 at 07:17

1 Answers1

1

Closing the Scanner closes its underlying System.in

To quote the Javadoc for Scanner#close:

If this scanner has not yet been closed then if its underlying readable also implements the Closeable interface then the readable's close method will be invoked.

The System.in object is an InputStream. And InputStream does indeed implement Closeable. So that transitive closing does apply.

So… Closing a Scanner object based on the System.in object closes both.

System.in cannot be reopened

Do not do that, do not close System.in. You cannot reopen System.in.

Generally, you should close resource such as Scanner. The Scanner class is even AutoCloseable to make such closing easy with try-with-resources syntax.

But if your scanner is based on System.in, then we have an exception to the always-close-your-resources rule.

For more info, see:

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
  • but opening it and closing inside the same method, shouldn't it be normally reopened at each new method call? I don't understand why the stream doesn't reopen – rainbow May 16 '23 at 07:06
  • 1
    @rainbow You cannot re-open `System.in`. – Basil Bourque May 16 '23 at 07:14
  • not sure if closing is the only problem here as in this code snippet I don't close, but as soon as I remove the userInput.next(); line it gets stuck in the loop again. – rainbow May 16 '23 at 07:32
  • @rainbow Your Question says that you closed it in other code, which you confusingly chose not to show us. – Basil Bourque May 16 '23 at 07:40
  • my question says what are the ways to avoid the error as my current way out was to take out the scanner object outside the loop and use next method, which I am not sure is the best way to solve the problem – rainbow May 16 '23 at 08:17
  • @rainbow if you remove the `userInput.next()`, then the scanner will not progress if the next input is not an integer or 'q', and therefore gets stuck in an infinite loop. Your problem description does not match the code you are showing, and does not match the real problem you are having. – fishinear May 16 '23 at 12:25