Java Exception Handling

Java Exception Handling – InputMismatchException

Moving along through our in-depth Java Exception Handling series, today we’ll be examining the InputMismatchException. The InputMismatchException is thrown when attempting to retrieve a token using the text Scanner class that doesn’t match the expected pattern or type.

In this article we’ll explore the InputMismatchException in more detail by first looking at where it sits in the larger Java Exception Hierarchy. We’ll also explore the basic purpose and usage of the built-in Scanner class, and see how improper use of this class can result in unintended InputMismatchExceptions in your own code, so let’s dig in!

The Technical Rundown

All Java errors implement the java.lang.Throwable interface, or are extended from another inherited class therein. The full exception hierarchy of this error is:

Full Code Sample

Below is the full code sample we’ll be using in this article. It can be copied and pasted if you’d like to play with the code yourself and see how everything works.

This code sample also uses the Logging utility class, the source of which can be found here on GitHub.

When Should You Use It?

To understand what might cause an InputMismatchException we need to briefly explore what the Scanner class is and how it might be used in a Java application. Scanner can be used to perform simple text scanning and parsing using regular expression or delimiter pattern matching. It can be used for single lines of text, or even massive files containing thousands of lines. For our sample code we’re using the 2014 GDP data courtesy of Plotly, which is in the common CSV file format. There are only a couple hundred lines of text in this file, but it should give us an interesting scenario in which to properly parse some real-world data.

To begin we start with the delimiterTestByDirectTypes method:

This method passes our local File into a new Scanner instance and specifies the use of a delimiter of "[,\\n]". This delimiter is necessary because our CSV file separates each value within a record using a comma, but it also contains multiple records separated by a newline (\n), so we need to inform the Scanner instance that both of these delimiters should be accounted for.

Once our Scanner instance is created we check if it has a new line and next() value, which is the default type retrieved by a Scanner instance and assumes the token it will find next is a String. From there, we directly extract each subsequent token by calling scanner.next() or scanner.nextDouble(), depending what type of value we’re extracting. Both our country and code values should be Strings, while gdp is a float or Double. Once extracted, we output everything into a clean, padded format.

The result of executing this method is as follows:

So, everything was working just as expected until we reached the 15th record, at which point an InputMismatchException was thrown. If we look at the source data a bit we can see why we ran into a problem:

Here we see our data isn’t as “clean” as we originally thought. The “The Bahamas” is alphabetized by ignoring the word “The”, this record actually has three comma delimiters, rather than the expected three of all previous records. As a result, our first call to scanner.next() for this record returns "Bahamas, because it uses the first comma it finds as a delimiter. Thus, the next call to scanner.nextDouble() tries to evaluate the value of The" as a Double, which obviously fails, resulting in the InputMismatchException we see above.

One possible solution would be to perform some kind of sanity checks before we explicitly call the scanner.nextDouble() method. To assist with this and allow our code to be a bit more future-proof we’ve added the getScannerValueByType(Class<T> clazz, Scanner scanner) helper method:

The purpose of this method is merely to invoke the appropriate scanner.next[Type] method based on the <T> type that was passed to it. Furthermore, we explicitly perform a sanity check using the appropriate scanner.hasNext[Type] method prior to actually returning a value, to ensure that no unexpected InputMismatchExceptions are thrown.

The delimiterTestByTypes() method is similar to our previous test, but we use the results of getScannerValueByType(...) for each record:

Executing this code no longer throws any InputMismatchExceptions, but we still see some strange behavior:

Once again, everything works fine until we get to the “Bahamas, The” record. Just as before, since our delimiter pattern is merely [,\\n], the Scanner has a difficult time handling records with more than two comma-separators. In fact, the best solution is not to rely on the delimiter setting at all, but to instead use a more complex RegEx pattern to match (and then extract) each value from each record. To accomplish this we have one last method, resultStreamTest():

Here we’re taking advantage of the new findAll(Pattern pattern) method added to the Scanner class in Java 9. This presents a much more modern programming pattern that allows us to chain functions, predicates, and other methods onto the iterable result of Stream<MatchResult>. In this case, we’re collecting all results into a List<MatchResult>, which we then iterate through in a for loop and extract each record’s value to be assigned to the relevant local variable and output.

The majority of the work here is accomplished in the REGEX_PATTERN passed to scanner.findAll(...):

You can see the regex pattern in action on regexr.com, but the basic purpose is to ensure we’re capturing only three values from each line, and that we handle all unusual formatting and characters that are possible in the country name/first field, such as extra commas, quotation marks, parenthesis, and so forth.

The final result is an accurate and clean extraction and log output of all our GDP data:

The Airbrake-Java library provides real-time error monitoring and automatic exception reporting for all your Java-based projects. Tight integration with Airbrake’s state of the art web dashboard ensures that Airbrake-Java gives you round-the-clock status updates on your application’s health and error rates. Airbrake-Java easily integrates with all the latest Java frameworks and platforms like Spring, Maven, log4j, Struts, Kotlin, Grails, Groovy, and many more. Plus, Airbrake-Java allows you to easily customize exception parameters and gives you full, configurable filter capabilities so you only gather the errors that matter most.

Check out all the amazing features Airbrake-Java has to offer and see for yourself why so many of the world’s best engineering teams are using Airbrake to revolutionize their exception handling practices!