In announcing OkHttp's new URL class, I wrote about how parsing returns null instead of throwing exceptions:
Instead,
parse()
just returns null when it doesn’t understand what you passed it.
Several developers thought this was a lousy API. Derek Morr tweeted:
"parse() just returns null" so, not sane.
Are you fucking kidding me? What is this, PHP?
Well, the method returns null because I think it’s the best tool for the job. Let’s review the options:
Checked Exceptions
I reject this option immediately because it punishes callers who know their input is valid.
Unchecked Exceptions
This is tempting. But now I’m punishing callers who expect that some of the inputs to parse()
to fail. For example, OkHttp’s own redirect handler needs to parse an arbitrary HTTP header, which should be an HTTP URL but could be anything.
public @Nullable Request followUpRequest() throws IOException {
String location = response.header("Location");
if (location == null) {
return null; // No "Location" header? No follow up.
}
HttpUrl redirectUrl = userRequest.httpUrl().resolve(location);
if (redirectUrl == null) {
return null; // Location header isn't an HTTP URL? No follow up.
}
...
return requestBuilder.url(redirectUrl).build();
Wrapping this code in try/catch
ceremony feels like work. It’s using exceptions for a non-exceptional case.
Optional<T>
In functional-programming nerd terms, this is isomorphic to returning null. People who really like Optional
can even use the API as-is:
String s = ...
Optional<HttpUrl> optionalHttpUrl = Optional.ofNullable(HttpUrl.parse(s));
The best thing about Optional
is that it is. Functional programming purists who cringe at keywords like null
and for
have the tools to compose a low-level API like HttpUrl.parse()
into a functor of their pleasing.
Null, like in Map.get().
In the programs I work on, NullPointerException
problems are rare.
We’ll call Map.get()
for a thing, and that thing is missing, and uh-oh, NullPointerException
. Or we add a new column to the database, and miss some rows during backfill, and then NullPointerException
. These problems are easy to isolate, and easy to fix.
The issue isn't null
; it’s that sometimes data is missing unexpectedly, and null
is just how such problems surface themselves. If the data is missing or the URL is bad either you’ll code to handle that beforehand because you expect that case in practice, or you won’t and the program will crash.
I like letting the program crash! Because for me, most of the time the data is present, and I don’t have to write a bunch of boilerplate to handle error cases that don’t ever materialize.