7

My class assignment is to write a program that has the user input a set of numerical values. If the user enters a value that is not a number, the program is supposed to give the user 2 second chances to enter a number correctly, and after those two chances, quit asking for input and print the sum of all values entered correctly so far.

As is, my code doesn't work quite right. When the first non-number is entered, the program executes the code in the catch block once, prints the "gimme input" line at the beginning of the try block but then immediately executes the code in the catch block again without waiting for the user to enter another number.

While perusing my textbook for clues, I noticed this line: "A NoSuchElementException is not caught by any of the catch clauses. The exception remains thrown until it is caught by another try block or the main method terminates."

Which is great, because now at least I know there's a good reason this is happening, but my textbook doesn't contain any further information on this quirk, and I haven't been able to find any understandable answers via StackOverflow or Google. So my question is two part:

a) How should I get around this for the purposes of this assignment?

b) What exactly does it mean that the exception is not caught by a catch clause? Isn't that what catch clauses exist for? I do want a solution to my assignment, but I also want to understand why this is the way it is, if possible.

Thanks for any help!

import java.util.InputMismatchException;
import java.util.NoSuchElementException;
import java.util.ArrayList;
import java.util.Scanner;

public class NotANumber {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        System.out.println("This program computes the sum of any number of real numbers. When you are done entering values, enter something other than a real number twice in a row.\n");

        ArrayList<Double> numbers = new ArrayList<Double>();

        int count = 0;
        while (count < 2) {
            try {
                System.out.println("Please enter a floating point number: ");
                double newNumber = in.nextDouble();
                numbers.add(newNumber);
                count = 0;
            }
            catch (NoSuchElementException exception) {
                System.out.println("The value entered was not correctly specified. All values must be integers or floating point values.");
                count++;
            }
        }

        if (!numbers.isEmpty()) {
            double sum = 0;
            for (Double each : numbers) {
                sum = sum + each;
            }
            System.out.println("The sum is " + sum);
        }
        else {
            System.out.println("There is no sum as no correctly specified values were entered.");
        }

    }
}
MeBigFatGuy
  • 28,272
  • 7
  • 61
  • 66
ellriley
  • 605
  • 2
  • 7
  • 21

5 Answers5

4

For now, forget about catching the exception (it isn't what you want to do!). What you want to do is add calls like: s.hasDouble() before calling s.nextDouble().

The reason you don't want to catch that exception is because it is a RuntimeException which is meant to be used to indicate a programmer mistake, one that you should fix.

The simple advice is don't catch exceptions that the compiler doesn't tell you to catch, instead if one of them is thrown figure out the change you need to make to your code so that exception doesn't happen.

For exceptions the compiler tells you that you must deal with them you do something like:

try
{
    foo();
}
catch(final IOException ex)
{
    // do something smarter here!
    ex.printStackTrace();
}

In that code, foo() is declared something like:

public void foo() 
    throws IOException 
{
   // ... code ...
}

IOException is a checked exception (RuntimeException, also called unchecked exceptions, should not be caught, checked exceptions must be caught... well you can do other things beyond catch, but for now don't worry about those).

So, long answer short, make the exception not happen by calling s.hasXXX() before calling s.nextXXX().

TofuBeer
  • 60,850
  • 18
  • 118
  • 163
  • Okay, thanks, that makes a lot of sense. This was a programming exercise at the end of a chapter all about exception handling, so I guess I just assumed I was supposed to catch instead of avoid having it thrown in the first place. I wish my textbook had made it clear that runtime exceptions aren't meant to be caught, that seems like a pretty important concept to not spell out for a beginner. Thanks again!!! – ellriley Apr 15 '11 at 04:07
  • short answer... books suck :-) Long answer... there is a lot to cover and throwing a whole lot of things at students all at once is not a good way to do it. – TofuBeer Apr 15 '11 at 05:30
  • @Erika E - the real reason that your textbook doesn't give this advice is ... IMO ... that it is incorrect advice! See my answer for an explanation. – Stephen C Apr 15 '11 at 07:08
  • A student who is just learning doesn't have the knowledge need to judge when it is "ok" to catch a runtime exception. Spelling out all the "rules" isn't viable as they will be hit with too much to remember. The simpler the rules the better up front. As the person gains more experience then it is time to gradually introduce them to the "exceptions" to the "rules". My students are basically told not to follow my unit tests (which catch Throwable) when looking at the solutions they are provided. In time they learn more and more about what to do in certain cases. – TofuBeer Apr 16 '11 at 02:20
1

You are mistaken: Scanner.nextDouble throws a NoSuchElementException if the input is exhausted, which is unlikely to happen with standard input (it will block instead). An incorrect value will produce an InputMismatchException.

My guess, however, is that nextDouble does not remove the offending value from the stream on failure. You'll need to "clear" the input in your catch before resuming the read.

Etienne de Martel
  • 34,692
  • 8
  • 91
  • 111
  • Thanks, this was also extremely helpful! I didn't think about not removing the value. I added "String trash = in.next();" to my new else (instead of catch) clause. – ellriley Apr 15 '11 at 04:27
1

@TofuBear states:

The simple advice is don't catch exceptions that the compiler doesn't tell you to catch, instead if one of them is thrown figure out the change you need to make to your code so that exception doesn't happen.

I think that's an over-simplification.

It is true that exceptions that are declared as checked exceptions HAVE to be either caught or declared as thrown in the method signature.

It is also true that you don't have to catch unchecked exceptions, and indeed that you should think carefully about whether it wise to catch an unchecked. (For instance, you should think whether the exception is being thrown at the point you expect and for the reasons that you expect.)

However, in some circumstances it is clearly necessary to catch them. For instance:

  try {
      System.out.println("Enter a lucky number!");
      String input = // get string from user
      int number = Integer.parseInt(input);
      ...
  } catch (NumberFormatException ex) {
      System.err.println("Bad luck!  You entered an invalid number");
  }

If you didn't catch NumberFormatException ... which is an unchecked exception ... then you wouldn't be in position to print out a friendly message, and ask the user to try again.

In short, unchecked exceptions don't always mean programmer error.

  • A few of the standard ones could indicate bad input from a user or client (e.g. NumberFormatException, IllegalArgumentException, ArithmeticException, etc), or they could indicate something that a specific application can recover from, or at least attempt to diagnose.

  • I've come across third party libraries where the designer has an aversion to checked exceptions and has declared all library exceptions as unchecked. (Bad design IMO, but it happens ...)

So a blanket statement that you shouldn't catch unchecked exceptions is clearly wrong advice.


However the second part of @TofuBear's advice is valid. It is (generally) a better idea to do a test to prevent an anticipated exception from happening than to do the action and catch the exception. (The code is typically simpler, and typically more efficient ... though there are counter examples.)

In this case, if you call and test hasNextDouble() before nextDouble() you can avoid the error case you are trying to handle with the try / catch.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
  • "I think that's an over-simplification." I was trying to keep it simple for someone who doesn't know about exceptions. Your example of NumberFormatException is a bad example. The parseInt function is broken - it should throw a checked exception, or there should be another method isInt(String) that you can call to avoid the exception. I personally use a regex to verify something is an int before calling parseInt. I'll change my blanket statement to you should only throw unchecked exceptions to indicate a programmer mistake (which, of curse, lets me keep my other blanket statement :-) – TofuBeer Apr 15 '11 at 05:35
  • Hey - it is irrelevant whether or not parseInt is "broken". The fact is that this is a common case where you DO need to catch an unchecked exception. And as such it is a good example. – Stephen C Apr 15 '11 at 06:58
  • *"I was trying to keep it simple for someone who doesn't know about exceptions."* It is a bad idea to simplify your message to the extent that you end up giving the wrong message. And this case, I thought it was sufficiently wrong that I spent a page of text in rebuttal. – Stephen C Apr 15 '11 at 07:06
  • No it isn't a good example... use a regex before parsing and then don't call parseInt if it is going to throw. – TofuBeer Apr 15 '11 at 14:49
  • Take a stab at this one please... http://stackoverflow.com/questions/613954/the-case-against-checked-exceptions – TofuBeer Apr 15 '11 at 14:51
  • @TofuBeer - you are missing my point. It is a good example of **why it can be necessary to catch unchecked exceptions**. And besides, using `Integer.parseInt` (without first checking with a regex) is not wrong ... apart from the circular argument that it is wrong because it throws an *unchecked* exception. Anyway, you won't persuade me that it is a bad example ... so lets just drop this. – Stephen C Apr 16 '11 at 01:59
  • @TofuBeer - I looked at that question. I have nothing substantial to add that hasn't been said. The only thing I might have added is that there is a real difference between the way that unchecked exceptions *should be* used, and the way that they *are* used. – Stephen C Apr 16 '11 at 02:04
  • I'll drop it... but my final word is simply, there is no need to catch a runtime exception in that case as there is a perfectly good solution to make the exception never occur. – TofuBeer Apr 16 '11 at 02:15
0

surround your statements around try..catch is a good idea when you have no clue that what will happen in real scenario. but i have alternative solution, most of the time we know this exception raise so you can avoid it using implementing iterator as follows..

for (ListIterator<Double> iter = numbers.listIterator(); iter.hasNext(); ) {
    if(iter.hasNext()) { // this return false in case of NoSuchElementException 
        // do your stuff here
    }
}
Muhammad Suleman
  • 2,892
  • 2
  • 25
  • 33
-2

You must change import.

Instead of

import java.util.NoSuchElementException;

use

import org.openqa.selenium.NoSuchElementException;
4b0
  • 21,981
  • 30
  • 95
  • 142