This is a part of this post:
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.
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.
goto
s. I would say they are Wormholes in the code which can teleport your execution point to places unexpected.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 NoAccessException(ACCESS_1);
}
if (!hasAccess2(userId)) {
throw new NoAccessException(ACCESS_2);
}
if (!hasAccess3(userId)) {
throw new NoAccessException(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 NoAccessException(ACCESS_1));
}
if (!hasAccess2(userId)) {
return Optional.of(new NoAccessException(ACCESS_2));
}
if (!hasAccess3(userId)) {
return Optional.of(new NoAccessException(ACCESS_3));
}
return Optional.empty();
}
Now, this function is turned into an isolated piece! 🏝
This functions 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"));
}
All Exceptions can be replaced with ADTs (Algebraic Data Types), unless they are exceptional. Few commonly used ADTs are:
some
OR none
left
OR right
With the advent of Pattern Matching in Java 17+, ADTs blend more naturally into the language.