2
votes

I have A string and B string , app prefers A if not null, and call transformation method changeA(). However if A is null , it will use B and invoke method changeB() . Both transformation method return same object type Result. However one thing to note is that str B might null too, then if that case return null instead.

How do I approach this using optional and lambda expression in JDK.

I have the following codebase:

changeA(){
..
}

changeB(){
..
}

aMethod(){
    Optional<String> optA=Optional.ofNullable(a);
    Result result = optA.map( aStr -> this.changeA( aStr ) ).orElse( this.changeB( bStr ) );
}

This looks a bit weird cause the orElse method is using variable not within the scope of the lambda. May I know the best approach to this ?


Perhaps I should state why I chose Optional. I want my team's developer to understand that even though String A is preferred , it might be null and must be handled since the part of the code is sensitive . String B is used for legacy part of the records only. Hopefully this clarify my intention on this approach.

2
If it's not in scope, how is it using it? - pvg
it is within the class it is executing - Seng Zhe
you don't need the filter step... I think the best approach is good old if-else or ?: :) - ZhongYu
Why try to over-complicate this into a single line for the sake of using a new, shiny thing? Just use if-else. - GManNickG
The primary use case for Optional is as a return value. That way developers are forced to handle the possible absence of a value, which should prevent NPEs. You, on the other hand, not only know that a can be null, but also have a method whose sole purpose is to handle that situation. Using an Optional here is somewhat pointless. - a better oliver

2 Answers

7
votes

To invoke changeB only when necessary you may use the following construct:

result = Optional.ofNullable(aStr).map(this::changeA).orElseGet(
          () -> Optional.ofNullable(bStr).map(this::changeB).orElse(null));

Note that you can do this without optionals at all:

result = aStr != null ? changeA(aStr) :
         bStr != null ? changeB(bStr) :
         null;

This is much shorter and generates cleaner bytecode.

An alternative functional way is to use the stream of suppliers:

Supplier<Result> aSupplier = () -> aStr == null ? null : changeA(aStr);
Supplier<Result> bSupplier = () -> bStr == null ? null : changeB(bStr);

result = Stream.of(aSupplier, bSupplier).map(Supplier::get)
               .filter(Objects::nonNull).findFirst().orElse(null);

This approach is scalable: you may add more suppliers to the stream. You may replace aStr == null ? null : changeA(aStr) with Optional.ofNullable(aStr).map(this::changeA).orElse(null) if you don't like ternary operator.

2
votes

You could use the following:

Optional<String> optA = Optional.ofNullable(aStr).map(this::changeA);
Optional<String> optB = Optional.ofNullable(bStr).map(this::changeB);
result = optA.orElse(optB.orElse(null));