DEV Community

Enrique Zamudio
Enrique Zamudio

Posted on

I hate checked exceptions

I hate checked exceptions. I really do. They should have really got rid of them in Java 8. They get in the way of a lot of the new stuff; it's not a coincidence that most of the other JVM languages don't have checked exceptions.

BEGIN RANT

I have a class in which I'm overriding an abstract method:

@Override
protected Runnable createTask(Request req) {
  Response resp = new Response(Errors.CANT_CONNECT);
  if (req.getType() == Request.Something) {
    return () -> {
      try {
        oneThing(req, resp);
      } catch (IOException ex) {
        log.error("Doing something with {}", req, ex);
      } finally {
        process(resp);
      }
    };
  } else if (req.getType() == Request.SomethingElse) {
    return () -> {
      try {
        somethingElse(req, resp);
      } catch (IOException ex) {
        log.error("doing something else with {}", req, ex);
      } finally {
        process(resp);
      }
    };
  } else if (req.getType() == Request.YetAnotherThing) {
    return () -> {
      try {
        anotherThing(req, resp);
      } catch (IOException ex) {
        log.error("Doing yet another thing with {}", req, ex);
      } finally {
        process(resp);
      }
    };
  }
  return null;
}

As you can see, it's very repetitive. A block is code is basically repeated 3 times with just a very small variation. An obvious opportunity to put it elsewhere, right? Something like this looks way nicer:

private Runnable execute(BiConsumer<Request, Response> m,
        Request req, String msg) {
  Response resp = new Response(Errores.CANT_CONNECT);
  return () -> {
    try {
      m.accept(req, resp);
    } catch (IOException ex) {
      log.error(msg, ex);
    } finally {
      process(resp);
    }
  };
}

@Override
public Runnable createTask(Request req) {
  if (req.getType() == Request.Something) {
    return ejecutar(this::oneThing, req, "Doing something with {}");
  } else if (req.getType() == Request.SomethingElse) {
    return ejecutar(this::somethingElse, req, "doing something else with {}");
  } else if (req.getType() == Request.YetAnotherThing) {
    return ejecutar(this::anotherThing, req, "Doing yet another thing with {}");
  }
  return null;
}

Way nicer. Yeah.

Except I can't do this. Why not? Well, BiConsumer doesn't throw IOException, therefore I can't catch it. Also, I can't pass refs to my methods because they throw IOException.

In order to do this, I need to catch IOException inside each of my methods, only to convert it into a RuntimeException, which I can then catch in execute.

I really don't want to do that right now. So... ⌘+Z ⌘+Z ⌘+Z ⌘+Z ⌘+Z ⌘+Z ⌘+Z ⌘+Z ⌘+Z ⌘+Z

END RANT

Top comments (4)

Collapse
 
tiguchi profile image
Thomas Werner •

You can define your own BiConsumer interface that throws checked exceptions as follows:

@FunctionalInterface
public interface CheckedBiConsumer<T, U, E extends Exception> {
    void accept(T arg1, U arg2) throws E;
}

And also define a CheckedRunnable that propagates the exception thrown by the CheckedBiConsumer:

@FunctionalInterface
public interface CheckedRunnable<E extends Exception> {
    void run() throws E;
}

That way you can leave it up to the code executing the runnable what to do with the checked exception.

Collapse
 
chochos profile image
Enrique Zamudio •

Tanks for the tip! Creating the CheckedBiConsumer does the trick indeed. I can't use the second one because the code I'm extending already defines that I need to return a standard Runnable though, but that's not a problem.

Collapse
 
kushal256 profile image
kushal256 •

This library contains many useful wrappers for Lambdas and Exceptions: github.com/jOOQ/jOOL#orgjooqlambda...

Collapse
 
chochos profile image
Enrique Zamudio •

I've heard about vavr, it's worth a look.