Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java API Design: NumberFormatException for Method that Parses a Semi-Numeric String?

I'm making a library that contains a few methods for parsing string dates and times. I am having difficulty deciding what exception those methods should throw when the string argument isn't parseable. I'm considering several options:

1. java.lang.IllegalArgumentException - an invalid string is clearly an illegal argument, but, to me, IllegalArgumentException typically means programming error, and it's rare to want to do an explicit try catch for one. I think string parsing is often for external input and is more of a special case that deserves special treatment. If, say, you had a big block of code that parsed user input and did something else with it, you might want to wrap that code in a try catch block so you could handle the case of the user input containing an invalid string. But catching IllegalArgumentException wouldn't be great for pinpointing invalid user input, because most likely there'd be multiple places in your code that it could be thrown from (not just the user-input parsing).

2. java.lang.NumberFormatException - it's thrown by Integer.parseInt(String) and other similar parsing methods in java.lang. So most Java developers are familiar with it being the exception to catch when you're trying to parse a string that may or may not be valid (e.g. user input). But it's got "Number" in its name, so I'm not sure it really fits with things like dates and times which are numeric in a sense, but conceptually different in my mind. If only it was called "FormatException"...

3. java.text.ParseException - not really an option because it's checked. I want unchecked for this.

4. A custom exception - this gets around the downsides of IllegalArgumentException and NumberFormatException, and it could extend IllegalArgumentException too. But I don't think it's a good idea to add exceptions to a library unless they're really needed. Quoting Josh Bloch, "If in doubt, leave it out". (Also, in my case, for a package that parses dates and times it's quite hard to name such an exception: "DateFormatException", "TimeFormatException", "CalendricalFormatException" (like JSR 310) - none seem ideal to me when applied to methods that parse dates, times, date-times etc. And I think it would be silly to create multiple exceptions in a single package if they were all just used to identify unparseable strings.)

So which option do you think makes most sense?

NB There are good reasons for me to not wanting to use java.util.Date or Joda Time, or JSR 310, so no need to suggest those. Plus I think it would be good if this question was kept fairly general, since this must be an issue that other people designing APIs have struggled with. Dates and times could just as well be IP addresses, or URLs, or any other kind of information that has string formats that need parsing.

Precedents Set by Other Libraries

Just a few examples I found:

java.sql.Timestamp.valueOf(String) throws IllegalArgumentException

java.util.Date.parse(String) throws IllegalArgumentException (deprecated method, and the exception isn't declared, but you can see it in the source)

java.util.UUID.fromString(String) throws IllegalArgumentException

org.apache.axis.types.Time(String) throws NumberFormatException (also Day class in Apache Axis)

org.apache.tools.ant.util.DeweyDecimal(String) throws NumberFormatException

com.google.gdata.data.DateTime.parseDateTime(String) throws NumberFormatException

java.lang.Package.isCompatibleWith(String versionString) throws NumberFormatException (in Java 7, this takes a string version number with dots - kind of like a date or a time in a sense)

I'm sure there are lots of packages that use a custom exception, so I doubt there's much point in giving examples of those.

like image 638
MB. Avatar asked Apr 15 '11 11:04

MB.


2 Answers

I would have suggested IllegalFormatException as base class (which inherits from IllegalArgumentException, but unfortunately the sole Constructor is Package-protected, so I'd probably use

  • IllegalArgumentException for a small API (a few methods)
  • your own Class Hierarchy that inherits from IllegalArgumentException otherwise.

In any case, I would use IllegalArgumentException as base class.

In one of my previous projects, the guideline was to only throw RuntimeExceptions that inherit from

  • IllegalStateException
  • IllegalArgumentException
  • UnsupportedOperationException

Although this isn't an official dogma, I'd call it a good practice. All three of these are simple and self-descriptive.

like image 62
Sean Patrick Floyd Avatar answered Nov 11 '22 04:11

Sean Patrick Floyd


I would opt for ParseException, because that's what DateFormat.parse throws. Thus it has the advantage you mention in 2., programmers being familiar with it. But since you want unchecked, I guess this is not an option. If forced to go for unchecked, I'll opt for 4.

like image 23
subsub Avatar answered Nov 11 '22 04:11

subsub