3

Why does the orElseThrow() method in Java take a Supplier as a parameter instead of an Exception? This is the question and the best answer I have found is in here, stating

The overarching concept here is that by providing a method reference (or a function) it can be lazily initialized. No need to create Exception object if you don't need it. Create it only when you need it.

However, I still do not understand it. Why can't the object creation be lazy?

Óscar López
  • 232,561
  • 37
  • 312
  • 386
Aria
  • 176
  • 2
  • 10
  • 6
    If you pass in an exception, then you must have created an exception, even if it doesn't end up being used. That's the opposite of lazy. – khelwood Nov 25 '21 at 22:46
  • @Aria Is it safe to assume you roughly know what lambdas are and how they work? – MC Emperor Nov 25 '21 at 22:54
  • @MC Emperor I would say I know a bit more, but yes I am in the studying phase. – Aria Nov 25 '21 at 22:55

1 Answers1

10

We're supposed to use orElseThrow() like this:

anOptional.orElseThrow(() -> new Exception())

Because the above does not create an Exception immediately: only if the optional is not present the Supplier lambda will be called, and at that moment the new exception will be instantiated. On the other hand, if we had this situation:

anOptional.orElseThrow(new Exception())

The exception would always be created, even if the optional was present, which would be costly. This is the reason why orElseThrow expects a Supplier<? extends X> with <X extends Throwable>, in other words: a lambda expression with no arguments that returns a new Throwable object when called.

We say it's "lazy" because the lambda's body only gets evaluated at the last possible moment when it's truly needed, but if it's never needed the lambda's body will never be executed.

Óscar López
  • 232,561
  • 37
  • 312
  • 386
  • Could you please clarify what exactly the lazy mechanism is by the compiler or JVM? I do not get it. In the same way that somewhere in the line we wait to see if it is present or not and then call the anonymous function, we (the compiler/JVM) can do the same exact thing and create the exception only if it is not present. Why the function reference is more convenient? I think I am a bit lost. – Aria Nov 25 '21 at 22:53
  • 2
    @Aria it's not like that, it's called "lazy" because `() -> new Exception()` is a _lambda_, a "promise" to return something that will only be fulfilled when called. If no one calls it, it will never evaluate its body, hence it's lazy: the value is calculated only when needed. Maybe you should read about `lambdas` and lazy evaluation, to understand why this works. – Óscar López Nov 25 '21 at 22:58
  • I think the point you're missing is that simply writing `() -> new Exception()` does *not* call the lambda, it just declares it. `orElseThrow` will call it only if the optional was not present. – Óscar López Nov 25 '21 at 22:59
  • 1
    Worth reading https://stackoverflow.com/questions/36343209/which-part-of-throwing-an-exception-is-expensive – tgdavies Nov 25 '21 at 23:27
  • 2
    @Aria *"we (the compiler/JVM) can do ... and create the exception only if it is not present."* - No, that is not possible: when calling a method all the arguments must have been evaluated `new Exception()` is doing that, while `() -> new Exception()` is just a *function* that is created, but not executed, as argument - the *function* will be called later, if needed [*function* like an instance implementing a functional interface, e.g. `Runnable` or a method reference] – user16320675 Nov 25 '21 at 23:33
  • @user16320675 Exactly, I think your answer is the true answer. The creation of function is less expensive than the an exception. And the fact that arguments are handled first. However, although not as directly as yours that is what I have indirectly understood from Oscar Lopez's answer as well. – Aria Nov 25 '21 at 23:50
  • 2
    @user16320675 well, in principle, such an optimization is possible, but it wouldn’t be a good idea to rely on the presence of such an optimization. – Holger Nov 26 '21 at 12:56
  • 2
    @user16320675 yes, that’s the JIT compiler’s duty. After inlining `orElseThrow`, which is a trivial method, it would be possible to detect that one code path never uses the throwable. Then, it would have to inline all constructors of the throwable, to ensure that they are side effect free. If all preconditions are met, the creation could be skipped. As said, that’s nothing developers should rely on. – Holger Nov 26 '21 at 13:05