Reputation: 21466
Here's an interesting quandary using Java's Optional.flatMap()
. I have a class that encapsulates a CURIE in the form "foo:bar"
or just "bar"
. I also have a class for registering prefixes to namespaces, as well as for keeping track of the default namespace. It has a method findNamespaceByPrefix()
returning an Optional<URI>
(as the prefix may not be registered) and a method getDefaultNamespace()
, also returning an Optional<URI>
(as there may not be a default namespace).
Given some CURIE I want to find the namespace. It's tempting to use this:
Optional<URI> namespace = curie.getPrefix()
.flatMap(this::findNamespaceByPrefix)
.or(this::getDefaultNamespace);
But that is wrong. The problem is that it returns the default namespace, even if there is a prefix but no namespace was associated with that prefix. We only want to get back the default namespace (if any) if there is no prefix indicated at all in the CURIE.
Here is a solution:
Optional<String> prefix = curie.getPrefix();
Optional<URI> namespace = prefix.isPresent()
? prefix.flatMap(this::findNamespaceByPrefix)
: getDefaultNamespace();
And I'm fine with that, but it strikes me as somewhat non-functional with its use of the ternary operator, and using Optional.isPresent()
usually indicates there is a better way to do things.
Do any Optional
experts have a better, more functional solution to "short-circuit" the flat mapping? (I have a hunch I could do something with several layers of wrapping and unwrapping Optional
using Optional:of
and Optional:get
in the pipeline, but is there something perhaps more elegant?)
Upvotes: 2
Views: 437
Reputation: 798
You check for null prefix
Optional<URI> namespace = prefix
.filter(Objects::nonNull)
.flatMap(this::findNamespaceByPrefix)
.or(this::getDefaultNamespace);
you can test it as below:
public static void main(String[] args) {
test(Optional.ofNullable(null));
test(Optional.ofNullable("yourprefix"));
}
private static void test(Optional<String> prefix) {
Optional<URI> namespace2 = prefix
.filter(Objects::nonNull)
.flatMap(p -> Optional.of(URI.create(p)))
.or(() -> Optional.of(URI.create("default")));
System.out.println(namespace2);
}
Upvotes: -1
Reputation: 18163
Instead of "flatMapping" you should keep your second level of optional:
Optional<URI> namespace = curie.getPrefix()
.map(Application::findNamespaceByPrefix)
.orElseGet(Application::getDefaultNamespace);
The map
operation maps a prefix, if present, into a wrapped Optional<Optional<URI>>
that is either empty (if the prefix is absent) or contains a Optional<URI>
that is either empty (if no namespace could be found for the prefix) or contains the actual namespace.
And the outer orElseGet
only gets called when the wrapping Optional
is empty and your prefix is therefore absent.
Upvotes: 5