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.