Faith Heath
Faith Heath

Reputation: 81

When using JOptionPane.showInputDialog the window disappears before I can input and it's breaking parseint()

Why do my JOptionPane.showInputDialog windows disappear before I can enter an input? I think it's what's causing my code to break. I've googled everything I can think of and nothing seems to apply to what I'm doing here. The error I get is:

Exception in thread "main" java.lang.NumberFormatException: For input string: ""
        at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:67)
        at java.base/java.lang.Integer.parseInt(Integer.java:678)
        at java.base/java.lang.Integer.parseInt(Integer.java:786)
        at ChiliToGo.collectOrder(ChiliToGo.java:25)
        at ChiliToGo.main(ChiliToGo.java:13)

I'm just in a beginner java course so It's probably really dumb what I'm doing.

I've tried formatting the string and moving the main method into a separate class to access the methods in a non-static way because they are the best solutions I got from the posts I found. I'm just really confused because the parseint shouldn't get the string until I press ok right? It's only there for a moment and I don't have a chance to enter the input

Any help would be appreciated, code is below. Sorry if I made any mistakes this is my first post

import javax.swing.JOptionPane;

public class ChiliToGo {    
    static int adultMeal = 7;
    static int kidsMeal = 4;
    static String adultMealOrder;
    static String kidsMealOrder;
    static int adultMealOrdered;
    static int kidsMealOrdered;
    static int totalPrice;

    public static void main(String[] args) {
        collectOrder();
        finalizeOrder();
    }

    public static void collectOrder() {

        adultMealOrder = JOptionPane.showInputDialog("Please enter the number of adult meals:");
        kidsMealOrder = JOptionPane.showInputDialog("Please enter the number of kids meals:");
        
        adultMealOrdered = Integer.parseInt(adultMealOrder);
        kidsMealOrdered = Integer.parseInt(kidsMealOrder);
    }

    public static void finalizeOrder() {
        for(int i = 0; i < adultMealOrdered; i++) {
            totalPrice = totalPrice + adultMeal;
        }
        for(int n = 0; n < kidsMealOrdered; n++) {
            totalPrice = totalPrice + kidsMeal;
        }

        JOptionPane.showInternalMessageDialog( null, "Your order total is: " + totalPrice, null, JOptionPane.INFORMATION_MESSAGE);
    }
}

Upvotes: 0

Views: 364

Answers (2)

MadProgrammer
MadProgrammer

Reputation: 347332

So, the error is telling you that the input value of "" can't be converted to int. You're not doing any validation on the String returned from showInputDialog and are simply, blindly, trying to convert it to an int, which is failing, because no input has been made (and even worse, the return value could be null).

Cavet

The following is a "conceptual" idea which encompasses the input/validation into a self contained and re-usable unit of work. This "concept" could be used for console input as well.

The intention isn't to "answer" the question directly, but offer (not only an answer), but a conceptual and re-usable workflow and encourage a different way of "thinking" when approaching these kind of issues.

Possible solution

There are any number of ways you "might" consider fixing this, but the basic idea is to:

  1. Check that the return value is not null
  2. Validate the return value to ensure it's actually an int value.

To this end, I might encapsulate the logic into a self contained unit of work, for example...

    public static Integer getIntFromUser(String prompt) {
        Integer result = null;
        boolean done = false;
        do {
            String value = JOptionPane.showInputDialog(prompt);
            // User pressed cancel
            if (value == null) {
                done = true;
            } else {
                Scanner scanner = new Scanner(value);
                if (scanner.hasNextInt()) {
                    result = scanner.nextInt();
                    done = true;
                } else {
                    JOptionPane.showMessageDialog(null, "Please enter a numeric value");
                }
            }
        } while (!done);
        return result;
    }

This will loop until the user either inputs a valid int value or presses Cancel (hence the reason it returns Integer instead of int, this way you can inspect the return value for null)

The collectOrder method would also change to look something more like...

public static boolean collectOrder() {
    boolean validUserInput = false;
    Integer inputValue = getIntFromUser("Please enter the number of adult meals:");
    if (inputValue != null) {
        // Only prompt for the kids meal if the adult value is valid,
        // otherwise it's likely that they clicked cancel
        adultMealOrdered = inputValue;
        inputValue = getIntFromUser("Please enter the number of kids meals:");
        if (inputValue != null) {
            kidsMealOrdered = inputValue;
            validUserInput = true;
        }
    }
    
    return validUserInput;
}

The intention here is to check the return result from getIntFromUser, if it's null, the user pressed <kbd>Cancel</kbd> and we should stop any further processing, otherwise we go onto the next step.

collectOrder also returns boolean to indicate whether it has all the required data or not, so we don't try and finalise the order with invalid or partial data.

Runnable example...

public static class ChiliToGo {

    static int adultMeal = 7;
    static int kidsMeal = 4;
    static int adultMealOrdered;
    static int kidsMealOrdered;
    static int totalPrice;

    public static void main(String[] args) {
        if (collectOrder()) {
            finalizeOrder();
        }
    }

    public static boolean collectOrder() {
        boolean validUserInput = false;
        Integer inputValue = getIntFromUser("Please enter the number of adult meals:");
        if (inputValue != null) {
            // Only prompt for the kids meal if the adult value is valid,
            // otherwise it's likely that they clicked cancel
            adultMealOrdered = inputValue;
            inputValue = getIntFromUser("Please enter the number of kids meals:");
            if (inputValue != null) {
                kidsMealOrdered = inputValue;
                validUserInput = true;
            }
        }
        
        return validUserInput;
    }

    public static Integer getIntFromUser(String prompt) {
        Integer result = null;
        boolean done = false;
        do {
            String value = JOptionPane.showInputDialog(prompt);
            // User pressed cancel
            if (value == null) {
                done = true;
            } else {
                Scanner scanner = new Scanner(value);
                if (scanner.hasNextInt()) {
                    result = scanner.nextInt();
                    done = true;
                } else {
                    JOptionPane.showMessageDialog(null, "Please enter a numeric value");
                }
            }
        } while (!done);
        return result;
    }

    public static void finalizeOrder() {
        for (int i = 0; i < adultMealOrdered; i++) {
            totalPrice = totalPrice + adultMeal;
        }
        for (int n = 0; n < kidsMealOrdered; n++) {
            totalPrice = totalPrice + kidsMeal;
        }

        JOptionPane.showInternalMessageDialog(null, "Your order total is: " + totalPrice, null, JOptionPane.INFORMATION_MESSAGE);
    }
}

But why not use a try-catch block?

Well, this might be a little old school, and you don't always have the choice, but try-catch blocks, generally, shouldn't be used as control flow mechanisms (every rule has an exception 😉)

Upvotes: 1

hfontanez
hfontanez

Reputation: 6188

Your problem is that you have no error handling. If any of the dialogs that set the adultMealOrder or kidsMealOrder is left blank, when you try to parse the integer value, it throws an exception. You must handle the exception and convert the blank String value to zero. Basically, the function cannot convert an empty string into a number. You must do it yourself.

try {
    adultMealOrdered = Integer.parseInt(adultMealOrder);
} catch (NumberFormatException nfe) {
    adultMealOrdered = 0;
}

Another way to handle this, is to check the output of the dialog function and continue to prompt until the output is not null or not empty.

while (adultMealOrder == null || adultMealOrder.isBlank()) {
    adultMealOrder = JOptionPane.showInputDialog("Please enter the number of adult meals:");
}

I personally rather add the error handling on the parseInt function rather than this, but it is an option nevertheless.


You have other issues:

  1. Your application runs forever. You have no way to close the application. So, every time you "run" the application, the previous executions are still idle in the background. You should create an application frame and open these dialogs from there. Once you close the frame (if you set EXIT_ON_CLOSE), this problem should be fixed.
  2. Don't loop to calculate the final price.
for(int i = 0; i < adultMealOrdered; i++) {
    totalPrice = totalPrice + adultMeal;
}
for(int n = 0; n < kidsMealOrdered; n++) {
    totalPrice = totalPrice + kidsMeal;
}

Should be simplified to this:

totalPrice = (adultMealOrdered * adultMeal) + (kidsMealOrdered * kidsMeal);

Upvotes: 0

Related Questions