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.
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)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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With