2

I am working on teaching myself java and while working on a code using classes I ran into this error

Exception in thread "main" java.util.NoSuchElementException
    at java.util.Scanner.throwFor(Unknown Source)
    at java.util.Scanner.next(Unknown Source)
    at java.util.Scanner.nextDouble(Unknown Source)
    at StateCalculator.getOperand(StateCalculator.java:29)
    at StateCalculator.main(StateCalculator.java:77)

Below is my code:

import java.util.Scanner;


public class StateCalculator {
    private double currentValue = 0;

    //Initialize to 0
    public StateCalculator() {
    }

    public static int displayMenu() {
        Scanner keyboard = new Scanner(System.in);
        int menuChoice = 0;

        do {
            System.out.print("Menu\n 1. Add\n 2. Subtract\n 3. Multiply\n 4. Divide\n 5.Clear\n 6. Quit\n What would you like to do?: ");
            menuChoice = keyboard.nextInt();
        } while(menuChoice < 1 || menuChoice > 6);

        keyboard.close();
        return menuChoice;
    }

    public static double getOperand(String prompt) {
        Scanner input = new Scanner(System.in);
        double operand = 0;

        System.out.print(prompt);
        operand = input.nextDouble();

        input.close();
        return operand;
    }

    public double getCurrentValue() {
        return currentValue;
    }

    public void add(double operand) {
        currentValue += operand;
    }

    public void subtract(double operand) {
        currentValue -= operand;
    }

    public void multiply(double operand) {
        currentValue *= operand;
    }

    public void divide(double operand) {
        if(operand == 0) {
            currentValue = Double.NaN;
        }
        else {
            currentValue /= operand;
        }
    }

    public void clear() {
        currentValue = 0;
    }


    public static void main(String[] args) {
        Scanner keyboard = new Scanner(System.in);
        StateCalculator calculator = new StateCalculator();
        int option;
        double operand;

        do{
            System.out.println("The current value is " + calculator.currentValue);
            option = StateCalculator.displayMenu();

            switch(option) {
            case 1:
                operand = getOperand("What is the second number?: ");
                calculator.add(operand);
                break;
            case 2:
                operand = getOperand("What is the second number?: ");
                calculator.subtract(operand);
                break;
            case 3:
                operand = getOperand("What is the second number?: ");
                calculator.multiply(operand);
                break;
            case 4:
                operand = getOperand("What is the second number?: ");
                calculator.divide(operand);
                break;
            case 5:
                calculator.clear();
                break;
            }

        }while(option != 6);

        keyboard.close();
    }

}

I tried running the debug feature in eclipse and discovered the problem occurs on line 29 in my getOperand method when I attempt to set operand = input.nextDouble. However, I don't understand why this would be an issue.

AlexR
  • 114,158
  • 16
  • 130
  • 208
ab91
  • 179
  • 16

1 Answers1

5

Don't call keyboard.close(); when you close keyboard (which you defined)

Scanner keyboard = new Scanner(System.in);

it closes System.in, and then your other methods can't work (because the console will not re-open). You can have multiple scanners on System.in (as long as you don't close them), or pass one (or, but please don't, use a global).

Per the javadoc,

When a Scanner is closed, it will close its input source if the source implements the Closeable interface.

Elliott Frisch
  • 198,278
  • 20
  • 158
  • 249
  • 1
    Why is it bad to use a global `new Scanner(System.in)`? I've done this before, which is why I'm curious. You even say that there's no need to close it. It's all going to the same place, so to me it makes sense to use one `Scanner`. – asteri Jul 09 '14 at 16:36
  • 1
    This answer is correct. See http://docs.oracle.com/javase/1.5.0/docs/api/java/util/Scanner.html#close(), where it reads "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.*" – Stefan Winkler Jul 09 '14 at 16:38
  • It's *generally* bad to use a global (especially when it could so easily be passed in). It's causing your error to close the `Scanner`, edited my answer to add a javadoc link. – Elliott Frisch Jul 09 '14 at 16:38
  • I understand that closing the `Scanner` causes the issue. But that has nothing to do with using a global. It's much easier to reference a global than pass it around to a bunch of different methods. That would just muddy up the method signatures for no reason. Just use the global and never close it. – asteri Jul 09 '14 at 16:39
  • @JeffGohlke Until you want to write to another OutputStream in one caller. Then it's a nightmare. I do agree that for a simple enough program with one operation and one destination it's not an unreasonable approach. But code has a way of being used in other ways at other times. – Elliott Frisch Jul 09 '14 at 16:41
  • I still don't get it. What's wrong with having a global `Scanner` for `System.in` and then simply creating something different in the methods where you want to write to a different stream? Seems pretty straightforward. – asteri Jul 09 '14 at 16:43
  • @JeffGohlke Depends. Who's writing the methods there? You? Or one of your off-shore colleagues? Or one of your customers? Or the (possibly ax-weilding) maintenance developer? – Elliott Frisch Jul 09 '14 at 16:45
  • I understand it being bad practice to use a global scanner, but why would closing my scanner be an issue if I created another in the method? – ab91 Jul 09 '14 at 16:54
  • @user2792317 Because it calls `close()` on `System.in` - do you know what `System.in` is? **A global**. :) – Elliott Frisch Jul 09 '14 at 16:55
  • Ok, I see now. But eclipse gives a warning that I didn't close my other inputs. How can I fix that? Or should I just ignore it because it's only a warning? – ab91 Jul 09 '14 at 17:05
  • @user2792317 Either ignore it, perhaps with an annotation. Or construct one Scanner and pass it to your methods (and then close it on exit), or (but please don't) use a global. – Elliott Frisch Jul 09 '14 at 17:07