0

I'm always getting the default NumberFormatException message when the variables d1 and d2 are not of the proper data type.

I want to print my custom Exception message when those exceptions are caught using the throw statement conditionals.

public static void main(String[] args) {

    Scanner sc = new Scanner(System.in);

    System.out.print("Enter a numeric value: ");
    String input1 = sc.nextLine();
    Double d1 = Double.parseDouble(input1);
    System.out.print("Enter a numeric value: ");
    String input2 = sc.nextLine();
    Double d2 = Double.parseDouble(input2);
    System.out.print("Choose an operation (+ - * /): ");
    String input3 = sc.nextLine();
    try {
        if (!(d1 instanceof Double)) {
            throw (new Exception("Number formatting exception caused by: "+d1));
        }
        if (!(d2 instanceof Double)) {
            throw (new NumberFormatException("Number formatting exception caused by: "+d2));
        }
        switch (input3) {
            case "+":
                Double result = d1 + d2;
                System.out.println("The answer is " + result);
                break;
            case "-":
                Double result1 = d1 - d2;
                System.out.println("The answer is " + result1);
                break;
            case "*":
                Double result2 = d1 * d2;
                System.out.println("The answer is " + result2);
                break; 
            case "/":
                Double result3 = d1 / d2;
                System.out.println("The answer is " + result3);
                break;
            default:
                System.out.println("Unrecognized Operation!");
                break;
        }
    }
    catch (Exception e){ 
        System.out.println(e.getMessage());
    }

}

}

This is an example of the message printed when entered value is not of proper format.

Enter a numeric value: $ Exception in thread "main" java.lang.NumberFormatException: For input string: "$" at java.base/jdk.internal.math.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:2054) at java.base/jdk.internal.math.FloatingDecimal.parseDouble(FloatingDecimal.java:110) at java.base/java.lang.Double.parseDouble(Double.java:543) at com.example.java.Main.main(Main.java:13)

Darnoc Eloc
  • 75
  • 11
  • That's because, as the stack trace points out, the error occurs in `parseDouble`, immediately upon you typing something that isn't a number. Execution never even makes it to "putting something into `d1`", the code throws, and thus exits `main()`, before the assignment can even take place. – Mike 'Pomax' Kamermans Sep 07 '19 at 16:42
  • The calls to [`Double#parseDouble(String)`](https://docs.oracle.com/en/java/javase/12/docs/api/java.base/java/lang/Double.html#parseDouble(java.lang.String)) will throw a `NumberFormatException` if the `String` argument cannot be parsed into a `Double`. These calls happen before you attempt to throw your own exceptions. Also, `d1` and `d2` will always be an instances of `Double` because their declared type is `Double` and they cannot be `null`—you never set them to `null` and `parseDouble` never returns `null` (the method actually returns the primitive type `double`). – Slaw Sep 07 '19 at 16:49

1 Answers1

6

The problem with your code is that you make the check well after you've already attempted to parse it.

Your main clue is the stack trace that you get. It clearly shows that the exception is thrown at parseDouble on line 13. This is well before you do your own check, and the program never reaches it.

Your next step should then be checking the Javadoc of Double.parseDouble to see what it returns and when it throws.

The docs read (I've left out some things irrelevant to the question):

Returns a new double initialized to the value represented by the specified String

Throws:
    NullPointerException - if the string is null
    NumberFormatException - if the string does not contain a parsable double.

What this means, is that if you use this method to parse your doubles, then it will throw on invalid input.

What you do next depends on what your goal is. The 2 main problems with throwing exceptions are:
- You want your custom exception, rather than the default
- You want to avoid extra exceptions (so as not to generate stack trace, etc)

If you're in the first category, then the solution is simple - put the parseDouble into a try block and rethrow a custom exception if it fails, like this:

double d1;
try {
    d1 = Double.parseDouble(input1);
catch (NumberFormatException | NullPointerException e) {
    throw new MyCustomException("Please enter a valid double!", e);
}

If you do this, then you don't need to check again in your try/switch blocks (You've already guaranteed the input is a valid type!)

If you want to avoid the NumberFormatException entirely, then your best bet is to follow the advice on the Javadoc and use a rege to precheck the string before attempting to parse.

Ordous
  • 3,844
  • 15
  • 25
  • The variables must be initialized outside of the try block or else they can't be called within subsequent scopes in the program. This is why I had the parseDouble statements before the try block in my initial posted code. – Darnoc Eloc Sep 07 '19 at 22:24
  • @DarnocEloc That is incorrect. They only need to be initialized on every valid path to a usage of that variable. If you rethrow in your catch block, then you can use them later on with no problems. – Ordous Sep 08 '19 at 23:28
  • Could you provide an implementation example of this concept? I enclosed the try catch blocks within a larger outer scope which allowed me to call the variables within the switch statements but not within the catch block. – Darnoc Eloc Sep 09 '19 at 02:51
  • @DarnocEloc The one in my answer works fine. You just need to *declare* outside the `try` block, *initialize* inside the `try` and *abort* (via `throw` or return`) in the `catch`. Then you can use the variable with no restrictions after. – Ordous Sep 09 '19 at 08:32
  • I either get a 'CustomException' cannot be resolved to type error or a unhandled exception type 'Exception' error when attempting to throw a new exception within the catch block. – Darnoc Eloc Sep 09 '19 at 16:04
  • @DarnocEloc You get the first when you don't define the exception class, and the second when you make it a checked exception (vs unchecked). If you want to use a custom exception without having to add `throws <...>` to the method signature, it has to subclass `RuntimeException`, and not `Exception`. Have a look at [this](https://stackoverflow.com/questions/27578/when-to-choose-checked-and-unchecked-exceptions) to help you choose between them. You might want to read up on exceptions in Java in general. – Ordous Sep 09 '19 at 17:15