I am trying to use Optional.or
to get an object of subclass A or, if empty, an object of subclass B:
interface Node {}
class InnerNode implements Node {}
class LeafNode implements Node {}
Optional<InnerNode> createInnerNode() {}
Optional<LeafNode> createLeafNode() {}
Optional<Node> node = createInnerNode().or(() -> createLeafNode());
When doing this, I get the following compiler error:
Bad return type in lambda expression: Optional<LeafNode> cannot be converted to Optional<? extends InnerNode>
If I instead use wildcards to explicitly tell the compiler that the Optionals contain an object extending from Node
:
Optional<? extends Node> optionalInner = createInnerNode();
Optional<? extends Node> optionalLeaf = createLeafNode();
Optional<Node> node = optionalInner.or(() -> optionalLeaf);
I get the following compiler error:
Bad return type in lambda expression: Optional<capture of ? extends Node> cannot be converted to Optional<? extends capture of ? extends Node>
The only way I found to make this work, is using an Optional.empty
in front:
Optional<Node> node = Optional.<Node>empty() // requires generic argument
.or(() -> createInnerNode())
.or(() -> createLeafNode());
But for me, this is confusing to read. Is it somehow possible to instruct the compiler to allow the statement optionalInner.or(() -> optionalLeaf)
? Or are there other alternatives to make this work?
The workaround you provided is a pretty simple answer, but you can convert an Optional<InnerNode>
to an Optional<Node>
trivially with map if you want to avoid a placeholder Optional.
createInnerNode.<Node>map(Function.identity()).or(() -> createLeafNode());
In general, this is also how you conceptually turn a List<Dog>
to a List<Animal>
or any generic collection to a different generic collection in java as well. It's somewhat annoying, but wildcards don't cut it because you want both read/write on the transformed collection.
The signature of or should tell you why your workaround works
Optional<T> or(Supplier<? extends Optional<? extends T>> supplier)
notice how it's already using the ? extends T in the supplier. Since the first empty Optional's T is Node, the or function can accept a supplier that returns
Optional<? extends Node>
and since both the innernode and leafnode are an Optional<? extends Node>
, this works.
So the problem is that you need an Optional<Node>
at some point.
I don't think you can avoid doing something since an Optional<InnerNode>
never type matches Optional<Node>
for reasons listed in other answers (and other questions on stack overflow).
Reading the other answer and trying
Optional<? extends Node> node = createInnerNode();
means that the T is a
? extends Node
which makes or
seem to want doubly nested captures? I can't stop getting
Bad return type in lambda expression: Optional<capture of ? extends Node> cannot be converted to Optional<? extends capture of ? extends Node>
when trying basically any cast with the following code
Optional<? extends Node> node = Node.createInnerNode();
node.or(() -> (Optional<? extends Node>) null);