9

In Java, I have recently began to adopt the Optional type more in my code. This allows for better null-value handling and to some extend also safer code. Optional has the ifPresentOrElse method which allows you to perform a specific action for when a value is present, or a specific action for when no value is present. However, this method does not allow you to declare a return type.

Is there an easy way I can use optionals and a ifPresentOrElse-like method to return values while unwrapping an Optional?

ImJustACowLol
  • 826
  • 2
  • 9
  • 27
  • 3
    To get return value, replace `opt.ifPresentOrElse(a, b)` with `r = opt.map(a).orElseGet(b)` – Andreas Mar 24 '20 at 21:24
  • @Andreas Just to note: That inolves converting `a` from a `Consumer` to a `Function` and `b` from a `Runnable` to a `Supplier`. – Slaw Mar 24 '20 at 23:14
  • @Slaw That would be a requirement of the goal to *return* a value, regardless of how they are used, i.e. it's not really the use of `map` or `orElseGet` that makes that requirement, it's a prerequisite of what OP wants. – Andreas Mar 24 '20 at 23:18
  • @Andreas Makes sense. I understood the question as wanting to perform actions on the `Optional` while unwrapping the _original_ value (whether it was present or not). Sort of like `also` in Kotlin. Your solution, while still capable of doing that, doesn't read the same. However, after seeing [the OP's answer](https://stackoverflow.com/a/60839319/6395627) I see they were after what you suggested. – Slaw Mar 24 '20 at 23:24

2 Answers2

6

Assuming following declarations (EDIT: note Consumer and Runnable are interfaces from @ImJustACowLol's self-answer, not java.util interfaces. For case the self-answer were deleted, the interfaces are same as those in java.util except the functional interface method has generic return type instead of void. Anyway the naming should have been chosen differently.):

Consumer<InputType, OutputType> yourConsumer = ...;
Runnable<OutputType> yourRunnable = ...;
Optional<InputType> yourOptional = ...;

you can compose following statement

OutputType result = yourOptional.map(yourConsumer).orElseGet(yourRunnable);

which admittedly doesn't fit in single method call, though still is concise and readable enough, IMHO.

Tomáš Záluský
  • 10,735
  • 2
  • 36
  • 64
  • Assuming `java.util.Optional`: (1) The `java.lang.Runnable` interface is not generic; (2) The `map` method expects a `java.util.function.Function`; (3) The `orElseGet` method expects a `java.util.function.Supplier`. – Slaw Mar 24 '20 at 23:06
  • @Slaw you are true of course, I assumed declarations on types declared in ImJustACowLol's answer. It's both mine and ImJustACowLol's mistake that I built my answer upon another one and s/he chosen such unlucky naming. I will edit answer to warn these types are not `java.util` types. – Tomáš Záluský Mar 25 '20 at 08:59
0

Unfortunately, subclassing Optional is impossible as all of it's initializers are private. So any solution would have to come for a class or type that is not a subclass of Optional. The convenience method + interfaces below provides the desired behavior.

public interface Consumer<InputType, OutputType> {
    public OutputType consume(InputType inputType);
}

public interface Runnable<OutputType> {
    public OutputType run();
}

public static <ReturnType, OptionalType> ReturnType ifPresentOrElse(Optional<OptionalType> optional, Consumer<OptionalType, ReturnType> consumer, Runnable<ReturnType> runnable) {
    return optional.isPresent() ? consumer.consume(optional.get()) : runnable.run();
}
ImJustACowLol
  • 826
  • 2
  • 9
  • 27
  • @Tomáš Záluský 's solution is a good compromise without adding boiler plate code. – IceMan Mar 24 '20 at 21:58
  • 1
    @IceMan The solution by Tomáš would fail to compile. – Slaw Mar 24 '20 at 23:12
  • 1
    Creating custom interfaces here is unnecessary. Your `Consumer` interface is effectively a `Function` and your `Runnable` interface is effectively a `Supplier`. With that in mind, you can use [Andreas' solution](https://stackoverflow.com/questions/60839318/java-optional-ifpresentorelse-with-return-value#comment107640929_60839318) which doesn't involve creating a utility method. Note that the answer by Tomáš is effectively the same solution as that suggested by Andreas except Tomáš uses the wrong types. – Slaw Mar 24 '20 at 23:17