Open Bocete opened 7 years ago
So, we have been thinking about this, but the approach we've been using, at least within Google, is a little surprising.
We tended to see reinventing java.util.function
or especially java.util.stream
as a non-starter. Additionally, the situation gets astronomically more complex if you have multiple checked exception types; you either need functional types for 2, 3, etc. checked exceptions, or you need to accept a common supertype of all your checked exceptions, which isn't really satisfactory.
So what we did instead was to introduce a class TunnelException
. It looks like an entirely conventional runtime exception that wraps a checked exception. It doesn't expose a constructor; there's a static method
<T> T tunnel(Callable<T> lambda)
...which runs lambda
and wraps any thrown exceptions in a TunnelException
. So far this isn't much different from just wrapping your checked exceptions in a RuntimeException
. It's just a specific type of runtime exception.
But what we did on top of that was add specialized support in Error Prone that enforces at compile time that you catch TunnelException
in the same method, and "un-tunnel" it: call TunnelException.getCauseAs(SpecificExceptionType.class)
to extract exactly the checked exception types that the lambda could throw. From there, you can rethrow them outside the method, handle them directly, or whatever you choose, just as a normal exception. So your checked exceptions go through a RuntimeException
"tunnel," and the compiler makes sure the tunnel comes out somewhere.
We haven't open-sourced this yet because it's in a somewhat weird position: it's a library that is usable anywhere, but its design is built around the expectation of compiler support from Error Prone to make sure you got the checked exceptions right. Without Error Prone, it's just wrapping checked exceptions in a runtime exception that's a little dressed up.
Runnable doesn't throw exceptions. Callable throws all Exceptions and has a return value, forcing one to declare a Callable argument in their code, and forcing users of the API to "return null" at the end of their lambdas for no good reason.
A ThrowingRunnable that throws a generic exception type would be really handy. Something like:
I don't know how many times I defined a ThrowingRunnable that throws a specific exception type. This seems pretty useful with Java 8 for helper utilities that take a lambda that throws something and throws that same thing in return. Here's a few ideas:
My first question should be, I guess, why doesn't this exist already. It's not a far fetched idea, somebody probably thought of this before. There must be a reason, and I'm curious about it.
This maybe seen as a step to reimplementing java.util.function to support throwing exceptions. I'm down with that too. It seems like the norm today is, when using java.util.function stuff with lambdas that throw exceptions, to wrap exceptions in runtime exceptions and unpack at the other side. That's a bad idea and nobody should be doing that. There should be a working alternative and Guava is the place to provide it.
If you think this would be a nice thing to have, I'll contribute.
Thanks!