1
\$\begingroup\$

I've been doing a lot of programs for classes that require a prompt from the program. I thought it would be much faster for me to rapidly test inputs if I didn't have to execute the program and then type the input once inside the program (as opposed to just hitting and adjusting the input that way).

My concern is this code seems to have some duplication with the exception handling, and I'm also not sure if it's as efficient as it can be. I'm somewhat of a beginner, but I'm certainly interested in more advanced ideas/solutions as that would point in direction of where to self-study more.

import java.util.Scanner;

/*
 *  This program is to implement testing command line input or reverting to regular prompts if
 *  an argument isn't provided or is invalid. It tests to see if the input is a negative integer,
 *  and displays the result.
 *
 *  Algorithm:
 *  If the console argument is valid, treat it as user input, or otherwise ignore it
 *  Continue to ask for a positive integer until one is given.
 *  Display the integer given and confirm it's negative
 */
public class TestConsoleArgs
{
    public static void main( String[] args )
    {
        String arg;
        Scanner user_input = new Scanner(System.in);
        int num = 0;

        try {
            arg = args[0];
            num = Integer.parseInt(arg);
        } catch (ArrayIndexOutOfBoundsException e) {
            arg = "";
        } catch (NumberFormatException e2) {
            arg = "";
        }

        while (num <= 0)
        {
            System.out.print("Please type in a positive integer.  ");
            arg = user_input.next();

            try {
                num = Integer.parseInt(arg);
            } catch (NumberFormatException e) {
                System.out.print("That's not an integer! ");
                continue;
            }

            if (num <= 0)
            {
                System.out.print("That's a negative number. ");
            }
        }


        System.out.println("Yes, '" + num + "' is a positive integer.");
    }
}
\$\endgroup\$
4
  • 3
    \$\begingroup\$ "I thought it would be much faster for me to rapidly test inputs if I didn't have to execute the program and then type the input once inside the program (as opposed to just hitting the up arrow and adjusting the input that way)." Are you perhaps looking for a way to do unit testing as well? A well-written method signature instead of 'doing everything' inside a main() method will be of some help too... \$\endgroup\$
    – h.j.k.
    Commented Sep 28, 2015 at 3:51
  • \$\begingroup\$ @h.j.k. I hadn't heard of unit testing before, although after reading the article, I guess that's kind of what I'm doing here. I'm mostly just focusing on how to best read and (safely) use input from the command line, which I intend to use elsewhere. The particular idea of testing to see if it's a positive number was just the simplest thing I thought of arbitrarily. Regarding factoring, I'm also not really sure how I'd be able to do that usefully in this case. \$\endgroup\$
    – BrainFRZ
    Commented Sep 28, 2015 at 3:59
  • 1
    \$\begingroup\$ Command line args are better than stdin prompts for scriptable programs, but you could solve you problem with echo foo | java MyClass. Or java MyClass <<< "some string", to make the interactive up-arrow cmdline editting slightly easier. (ctrl-a beginning of line, alt-d delete forward word would set you up for typing a new "foo" if you're using a terminal & shell with nice cmdline editing, e.g. bash. See unix.stackexchange.com/a/231671/79808 for more tips. These should work in a cygwin terminal in Windows, too, and maybe even mingw.) \$\endgroup\$ Commented Sep 28, 2015 at 4:21
  • 1
    \$\begingroup\$ BTW, strictly speaking, 0 is neither a positive or negative number. Similar to your solution, I've made an assumption in my answer but I treat it as positive. YMMV. ;) \$\endgroup\$
    – h.j.k.
    Commented Sep 28, 2015 at 7:52

2 Answers 2

2
\$\begingroup\$

I wouldn't use the variable name arg, input or argument would be preferred due to clarity and distinct nature from the conventional args in the main method.

You're over-complicating things, you can eliminate the need to check array bounds by evaluating and running based on args.length.

Here's a simple example:

import java.util.Scanner;

public class Add {
    public static void main(String[] args) {
        if (args.length == 2) {
            try {
                System.out.println("Result: " + 
                    (Double.parseDouble(args[0]) + Double.parseDouble(args[1]))
                );
                System.exit(0);
            } catch (NumberFormatException nfe) {/*Proceed*/}
        }

        Scanner input = new Scanner(System.in);

        System.out.print(
            "Simple example- adding two numbers\n"
            + "Enter the first number: "
        );
        double alpha = validate(input);

        System.out.print("Enter the second number: ");
        double beta = validate(input);

        System.out.println("The result is " + (alpha + beta));
        input.close();
    }

    private static double validate(Scanner input) {
        String userEntry = input.next();

        while (!isNumber(userEntry)) {
            System.out.print("Not a number, enter again: ");
            userEntry = input.next();
        }
        return Double.parseDouble(userEntry);
    }

    private static boolean isNumber(String input) {
        try {
            Double.parseDouble(input);
            return true;
        } catch (NumberFormatException nfe) {
            return false;
        }
    }
}

If the user gives the expected length of valid input run the shorter version. Otherwise, run through the program, including the error checking.

\$\endgroup\$
2
  • \$\begingroup\$ This is easily breakable if a user wants to be silly and enter a letter, which is what I was trying to prevent. If the user types java Add d f from the command line, they get: Exception in thread "main" java.lang.NumberFormatException: For input string: "d " at sun.misc.FloatingDecimal.readJavaFormatString(Unknown Source) at sun.misc.FloatingDecimal.parseDouble(Unknown Source) at java.lang.Double.parseDouble(Unknown Source) at Test.main(Test.java:10) \$\endgroup\$
    – BrainFRZ
    Commented Sep 28, 2015 at 4:06
  • 1
    \$\begingroup\$ Understood. I've edited it to give you more ideas, try again. \$\endgroup\$
    – Legato
    Commented Sep 28, 2015 at 4:49
3
\$\begingroup\$

user_input can be better renamed as userInput.

If you want to make the prompting 'self-contained', put it inside a method that will re-prompt the user for non-integer values:

private static int getIntegerInput(Scanner scanner) { ... }

I also don't think you need to catch ArrayIndexOutOfBoundsException, instead consider picking up from the command line only if args.length is at least 1:

// calls getIntegerInput(Scanner) with scanner if input is not an integer
private static int parseStringOrPrompt(String input, Scanner scanner) { ... }

Putting it altogether:

public static void main(String[] args) {
    try (Scanner scanner = new Scanner(System.in)) {
        int num = args.length > 0 ? parseStringOrPrompt(args[0], scanner) 
                                    : getIntegerInput(scanner);
        while (num < 0) {
            System.out.print("That's a negative integer.");
            num = getIntegerInput(scanner);
        }
        System.out.println("Yes, '" + num + "' is a positive integer.");
    }
}
\$\endgroup\$
2
  • \$\begingroup\$ Using try-with-resources on a Scanner with System.in will close System.in at the end of the block. That may not be desired. \$\endgroup\$
    – Johnbot
    Commented Sep 28, 2015 at 7:47
  • \$\begingroup\$ @Johnbot right, it should only be done in the main() method. \$\endgroup\$
    – h.j.k.
    Commented Sep 28, 2015 at 7:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.