2

I've been learning about concurrency and the streams API and came across this. The offerLast()method can throw InterruptedException, so I get that I must handle it. What I don't get is why can't I throw it at the method level by adding throws Exception?. As it is this code does not compile.

static BlockingDeque<Integer> queue = new LinkedBlockingDeque<>();

public static void testing() throws Exception {
    IntStream.iterate(1, i -> i+1).limit(5)
            .parallel()
            .forEach(s -> queue.offerLast(s, 10000, TimeUnit.MILLISECONDS));
}

I know it can be solved by surrounding it in a try/catch, or by creating a wrapper method that handles the error, but I'm still trying to understand why it can't be thrown at the method level.

DraegerMTN
  • 1,001
  • 2
  • 12
  • 21
  • See https://stackoverflow.com/questions/18198176/java-8-lambda-function-that-throws-exception – Ori Marko Nov 11 '18 at 09:39
  • 2
    Because forEach() expects a Consumer instance. And the accept() method of Consumer can't throw any checked exception: https://docs.oracle.com/javase/8/docs/api/java/util/function/Consumer.html – JB Nizet Nov 11 '18 at 09:40
  • 1
    Thanks a lot, for some reason I wasn't thinking of it as a Consumer. That makes sense now – DraegerMTN Nov 11 '18 at 09:44

2 Answers2

3

Because lambda expressions are not always evaluated immediately.

Let's you have this:

public Supplier<String> giveMeASupplier() throws Exception {
    return () -> someMethodThatThrowsCheckedException()
}

According to you, the above would work. Right?

Now in another method, I can do this:

Suppler<String> supplier = null;
try {
    supplier = giveMeASupplier() // no exception is thrown here.
} catch (Exception ex) {
    ex.printStackTrace();
}
if (supplier != null) {
    System.out.println(supplier.get()); // this might throw an exception! Yet it's not in a try...catch!
}

Now what do you think would happen if supplier.get() throws an exception? Is there anything to catch it? No. If somehow the catch block a few lines before gets run, then it would be really weird.

Sweeper
  • 213,210
  • 22
  • 193
  • 313
2

The simple answer is that the "method" you're referring to is Consumer.accept, not YourClass.testing.

The lambda s -> queue.offerLast(s, 10000, TimeUnit.MILLISECONDS) is an implementation of java.util.function.Consumer.accept(T), which doesn't declare that it can throw InterruptedException.
And this behavior is not particular to streams, wherever a lambda expression is defined, it must comply with the signature of the abstract method of the functional interface it implements.

ernest_k
  • 44,416
  • 5
  • 53
  • 99