Overfullstack Overfullstack
Design

Throw away Exceptions

Exceptions are Wormholes

This is a part of this post: Huh? to Aha! - A Refactoring Story

Exceptions are the most abused language feature

Although exceptions are introduced into Java language with good intentions, a developer can catch, throw, or duck any exception, making them the most abused language feature.

try {
...
} catch(NullPointerException | IllegalArgumentException | IndexOutOfBoundsException e) {
e.printStackTrace();
}
try {
...
} catch(Exception e) {
throw e;
}

You need to hunt down all throws and re-throws and multiple catch blocks, playing the dangerous game of inheritance.

Exceptions are Wormholes

ides

Whenever you are reading someone’s code or your own code after a while for that matter, 2 IDEs run, one on your laptop and another in your head. The laptop IDE is the one that takes its sweet time to index and gobbles up half your ram, (they got too greedy these days). The other one, the poor one, which runs in your skull, slowly reads line-by-line, and tries to make-up a picture of what’s going-on until that picture is suddenly shattered with a throw. If you’re like me with an extremely limited working memory and got ADHD on top of it, please don’t throw exceptions.

This poor method is trying to convey a result that something is wrong, or Nothing is wrong. But it’s struggling to convey this effect of absence and resorted to throws, because of which, this is tightly latched, not just to its caller but the entire call-hierarchy.

void verifyUserAccess(String userId) throws NoAccessException {
if (!hasAccess1(userId)) {
throw new NoAccessException1(ACCESS_1);
}
if (!hasAccess2(userId)) {
throw new NoAccessException2(ACCESS_2);
}
if (!hasAccess3(userId)) {
throw new NoAccessException3(ACCESS_3);
}
}

In this case, Optional is the right fit as a return type to represent that effect of absence.

Optional<NoAccessException> verifyUserAccess(String userId) {
if (!hasAccess1(userId)) {
return Optional.of(new NoAccessException1(ACCESS_1));
}
if (!hasAccess2(userId)) {
return Optional.of(new NoAccessException2(ACCESS_2));
}
if (!hasAccess3(userId)) {
return Optional.of(new NoAccessException3(ACCESS_3));
}
return Optional.empty();
}

Now, this function is turned into an isolated piece! 🏝

Exceptions misused to return multiple data types

This function has to return 2 data types, out of which it chose to use Exception for one of them.

int parse(String s) {
if (s.matches("-?[0-9]+")) {
return Integer.parseInt(s);
}
throw new NumberFormatException("Not a valid integer");
}

An Either should be the right return type to represent the intent without exceptions.

Either<NumberFormatException, Integer> parse(String s) {
if (s.matches("-?[0-9]+")) {
return Either.right(Integer.parseInt(s));
}
return Either.left(new NumberFormatException("Not a valid integer"));
}

Replace Exceptions with ADTs

With the advent of Pattern Matching in Java 17+, ADTs blend more naturally into the language.

My Talks on this

Back to all articles