8

Why a checked Exception can't be thrown in the orElse part when using ifPresentOrElse java Optional method ?

For example:

dao.findBook(id).ifPresentOrElse(book -> {
    printingService.print(book, printerName);
    changeBookPrintDate(book.getId(), LocalDateTime.now());
}, () -> new BookNotFoundException());

where BookNotFoundException is a custom exception extending Exception class (checked exception).

but this code makes the compiler upset:

unreported exception com...exception.BookNotFoundException; must be caught or declared to be thrown

(knowing that it is already thrown in the method declaration, and surrounding this block with try catch doesn't resolve the compilation problem).

But if we make BookNotFoundException to be extending RuntimeException (which is unchecked), then all works perfectly.

Anyone knows why ?

What's the reason that prevents throwing such kind of exceptions in this java 9 Optional method ?

And why should I make my exception a RuntimeException to make it works while it is more to be considered a 'custom exception' more than it is 'runtime' ?

This question is addressing the same problem but for java 8 lambdas, so I don't know if the same applies for java 9 Optionals.

Other researches leaded nowhere other than confirming that it is not possible.

Any Idea ?

hd84335
  • 8,815
  • 5
  • 34
  • 45

2 Answers2

25

You must add the throw with square brackets:

*() -> { throw new BookNotFoundException(); }*
ALUFTW
  • 1,914
  • 13
  • 24
16

The method ifPresentOrElse is a useful tool when you want to specify two non-throwing actions, so that you can continue after the method invocation in either case. But when you want to throw in the case of absent values, it’s an unnecessary complication. Just use

var book = dao.findBook(id).orElseThrow(BookNotFoundException::new);
printingService.print(book, printerName);
changeBookPrintDate(book.getId(), LocalDateTime.now());

Since this construct will throw for an absent value, the code flow will only continue for present values. So you can use book in the follow-up code like an ordinary local variable, but with the guaranty that it will never be null.

The method orElseThrow expects a Supplier for the exception to be thrown, instead of a function that throws. That way, the supplier can construct a checked exception without throwing it and the generic signature of orElseThrow declares to throw whatever type the supplier produced.

public <X extends Throwable> T orElseThrow​(Supplier<? extends X> exceptionSupplier) throws X

Holger
  • 285,553
  • 42
  • 434
  • 765
  • Thanks for the answer, yes I can use orElseThrow as you said, but my question was exactly on java-9 `ifPresentOrElse` which works for unchecked exceptions in the case of absent values, but not with checked ones. So just to understand why and if there is an alternative provided in java9 Optionals. Thanks – hd84335 Jul 18 '20 at 10:44
  • 3
    The technical reason is that you are passing a `Runnable` and its `run()` method does not declare any checked exceptions, hence, you cannot throw checked exceptions. The semantic reason, to use a `Runnable` which doesn’t support checked exceptions, has been given in the answer; it is not the intended use case. The alternative, the intended way to use it, has also been given in the answer already. – Holger Jul 27 '20 at 13:22