Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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 to java.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: either Factory 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 as final 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?

like image 692
Lyubomyr Shaydariv Avatar asked Dec 26 '12 11:12

Lyubomyr Shaydariv


People also ask

How does lambda expression handle checked exceptions?

The most straightforward way would be to use a try-catch block, wrap the checked exception into an unchecked exception and rethrow it: List<Integer> integers = Arrays. asList(3, 9, 7, 0, 10, 20); integers. forEach(i -> { try { writeToFile(i); } catch (IOException e) { throw new RuntimeException(e); } });

Is it mandatory to declare type of parameter in lambda expression?

A lambda expression is characterized by the following syntax. Following are the important characteristics of a lambda expression. Optional type declaration − No need to declare the type of a parameter. The compiler can inference the same from the value of the parameter.

Can we handle exception in lambda expression?

A lambda expression body can't throw any exceptions that haven't specified in a functional interface. If the lambda expression can throw an exception then the "throws" clause of a functional interface must declare the same exception or one of its subtype.

Can we throw checked exception in lambda expression?

We can pass lambda expressions that can throw a (checked) exception to the map, filter, forEach and other operations.


2 Answers

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);     } } 
like image 85
JB Nizet Avatar answered Sep 28 '22 16:09

JB Nizet


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.

like image 35
Edwin Dalorzo Avatar answered Sep 28 '22 15:09

Edwin Dalorzo