All Articles

Throw away Exceptions 👽

This is a part of this post:

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.

  • Exceptions are famously called modern gotos. I would say they are Wormholes in the code which can teleport your execution point to places unexpected.
  • And I am not even going into Async and Concurrent realms, where you add an extra dimension to this catching game called Threads.
  • Therefore, modern JVM languages like Kotlin are going away from the concept of Checked Exceptions. You shouldn’t force developers to catch exceptions, they will if they need to handle.

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! 🏝

Exceptions misused to return multiple data types

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"));
}

Replace Exceptions with ADTs

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

My Talks on this

  • 🇪🇸 JBCN Conf, 2021, Barcelona, Spain.

Published 7 Jul 2021

Join the Newsletter

Subscribe to get my latest content by email.

    I won't send you spam.

    Unsubscribe at any time.