Java 8: Mandatory checked exceptions handling in lambda expressions. Why mandatory, not optional?
I'm playing with the new lambda features in Java 8, and found that the practices offered by Java 8 are really useful. However, I'm wondering is there a good way to make a work-around for the following scenario. Suppose you have an object pool wrapper that requires some kind of a factory to fill the object pool, for example (using java.lang.functions.Factory
):
public class JdbcConnectionPool extends ObjectPool<Connection> {
public ConnectionPool(int maxConnections, String url) {
super(new Factory<Connection>() {
@Override
public Connection make() {
try {
return DriverManager.getConnection(url);
} catch ( SQLException ex ) {
throw new RuntimeException(ex);
}
}
}, maxConnections);
}
}
After transforming the functional interface into lambda expression, the code above becomes like that:
public class JdbcConnectionPool extends ObjectPool<Connection> {
public ConnectionPool(int maxConnections, String url) {
super(() -> {
try {
return DriverManager.getConnection(url);
} catch ( SQLException ex ) {
throw new RuntimeException(ex);
}
}, maxConnections);
}
}
Not so bad indeed, but the checked exception java.sql.SQLException
requires a try
/catch
block inside the lambda. At my company we use two interfaces for long time:
-
IOut<T>
that is an equivalent tojava.lang.functions.Factory
; - and a special interface for the cases that usually require checked exceptions propagation:
interface IUnsafeOut<T, E extends Throwable> { T out() throws E; }
.
Both IOut<T>
and IUnsafeOut<T>
are supposed to be removed during migration to Java 8, however there is no exact match for IUnsafeOut<T, E>
. If the lambda expressions could deal with checked exceptions like they were unchecked, it could be possible to use simply like the following in the constructor above:
super(() -> DriverManager.getConnection(url), maxConnections);
That looks much cleaner. I see that I can rewrite the ObjectPool
super class to accept our IUnsafeOut<T>
, but as far as I know, Java 8 is not finished yet, so could be there some changes like:
- implementing something similar to
IUnsafeOut<T, E>
? (to be honest, I consider that dirty - the subject must choose what to accept: eitherFactory
or "unsafe factory" that cannot have compatible method signatures) - simply ignoring checked exceptions in lambdas, so no need in
IUnsafeOut<T, E>
surrogates? (why not? e.g. another important change: OpenJDK, that I use,javac
now does not require variables and parameters to be declared asfinal
to be captured in an anonymous class [functional interface] or lambda expression)
So the question is generally is: is there a way to bypass checked exceptions in lambdas or is it planned in the future until Java 8 is finally released?
Update 1
Hm-m-m, as far as I understand what we currently have, it seems there is no way at the moment, despite the referenced article is dated from 2010: Brian Goetz explains exception transparency in Java. If nothing changed much in Java 8, this could be considered an answer. Also Brian says that interface ExceptionalCallable<V, E extends Exception>
(what I mentioned as IUnsafeOut<T, E extends Throwable>
out of our code legacy) is pretty much useless, and I agree with him.
Do I still miss something else?
Solution 1:
Not sure I really answer your question, but couldn't you simply use something like that?
public final class SupplierUtils {
private SupplierUtils() {
}
public static <T> Supplier<T> wrap(Callable<T> callable) {
return () -> {
try {
return callable.call();
}
catch (RuntimeException e) {
throw e;
}
catch (Exception e) {
throw new RuntimeException(e);
}
};
}
}
public class JdbcConnectionPool extends ObjectPool<Connection> {
public JdbcConnectionPool(int maxConnections, String url) {
super(SupplierUtils.wrap(() -> DriverManager.getConnection(url)), maxConnections);
}
}
Solution 2:
In the lambda mailing list this was throughly discussed. As you can see Brian Goetz suggested there that the alternative is to write your own combinator:
Or you could write your own trivial combinator:
static<T> Supplier<T> exceptionWrappingSupplier(Supplier<T> b) { return e -> { try { b.accept(e); } catch (Exception e) { throw new RuntimeException(e); } }; }
You can write it once, in less that the time it took to write your original e-mail. And similarly once for each kind of SAM you use.
I'd rather we look at this as "glass 99% full" rather than the alternative. Not all problems require new language features as solutions. (Not to mention that new language features always causes new problems.)
In those days the Consumer interface was called Block.
I think this corresponds with JB Nizet's answer.
Later Brian explains why this was designed this way (the reason of problem)
Yes, you'd have to provide your own exceptional SAMs. But then lambda conversion would work fine with them.
The EG discussed additional language and library support for this problem, and in the end felt that this was a bad cost/benefit tradeoff.
Library-based solutions cause a 2x explosion in SAM types (exceptional vs not), which interact badly with existing combinatorial explosions for primitive specialization.
The available language-based solutions were losers from a complexity/value tradeoff. Though there are some alternative solutions we are going to continue to explore -- though clearly not for 8 and probably not for 9 either.
In the meantime, you have the tools to do what you want. I get that you prefer we provide that last mile for you (and, secondarily, your request is really a thinly-veiled request for "why don't you just give up on checked exceptions already"), but I think the current state lets you get your job done.
Solution 3:
September 2015:
You can use ET for this. ET is a small Java 8 library for exception conversion/translation.
With ET you can write:
super(() -> et.withReturningTranslation(() -> DriverManager.getConnection(url)), maxConnections);
Multi line version:
super(() -> {
return et.withReturningTranslation(() -> DriverManager.getConnection(url));
}, maxConnections);
All you need to do before, is creating a new ExceptionTranslator
instance:
ExceptionTranslator et = ET.newConfiguration().done();
This instance is thread safe an can be shared by multiple components. You can configure more specific exception conversion rules (e.g. FooCheckedException -> BarRuntimeException
) if you like. If no other rules are available, checked exceptions are automatically converted to RuntimeException
.
(Disclaimer: I am the author of ET)
Solution 4:
We developed an internal project in my company that helped us with this. We decided to went public two months ago.
This is what we came up with:
@FunctionalInterface
public interface ThrowingFunction<T,R,E extends Throwable> {
R apply(T arg) throws E;
/**
* @param <T> type
* @param <E> checked exception
* @return a function that accepts one argument and returns it as a value.
*/
static <T, E extends Exception> ThrowingFunction<T, T, E> identity() {
return t -> t;
}
/**
* @return a Function that returns the result of the given function as an Optional instance.
* In case of a failure, empty Optional is returned
*/
static <T, R, E extends Exception> Function<T, Optional<R>> lifted(ThrowingFunction<T, R, E> f) {
Objects.requireNonNull(f);
return f.lift();
}
static <T, R, E extends Exception> Function<T, R> unchecked(ThrowingFunction<T, R, E> f) {
Objects.requireNonNull(f);
return f.uncheck();
}
default <V> ThrowingFunction<V, R, E> compose(final ThrowingFunction<? super V, ? extends T, E> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
default <V> ThrowingFunction<T, V, E> andThen(final ThrowingFunction<? super R, ? extends V, E> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
/**
* @return a Function that returns the result as an Optional instance. In case of a failure, empty Optional is
* returned
*/
default Function<T, Optional<R>> lift() {
return t -> {
try {
return Optional.of(apply(t));
} catch (Throwable e) {
return Optional.empty();
}
};
}
/**
* @return a new Function instance which wraps thrown checked exception instance into a RuntimeException
*/
default Function<T, R> uncheck() {
return t -> {
try {
return apply(t);
} catch (final Throwable e) {
throw new WrappedException(e);
}
};
}
}
https://github.com/TouK/ThrowingFunction/