Let\'s say I have two classes and two methods:
class Scratch {
private class A{}
private class B extends A{}
public Optional getItems(List&
An Optional
is not a subtype of Optional
. Unlike other programming languages, Java’s generic type system does not know “read only types” or “output type parameters”, so it doesn’t understand that Optional
only provides an instance of B
and could work at places where an Optional
is required.
When we write a statement like
Optional o = Optional.of(new B());
Java’s type inference uses the target type to determine that we want
Optional o = Optional.of(new B());
which is valid as new B()
can be used where an instance of A
is required.
The same applies to
return Optional.of(
items.stream()
.map(s -> new B())
.findFirst()
.get()
);
where the method’s declared return type is used to infer the type arguments to the Optional.of
invocation and passing the result of get()
, an instance of B
, where A
is required, is valid.
Unfortunately, this target type inference doesn’t work through chained invocations, so for
return items.stream()
.map(s -> new B())
.findFirst();
it is not used for the map
call. So for the map
call, the type inference uses the type of new B()
and its result type will be Stream
. The second problem is that findFirst()
is not generic, calling it on a Stream
invariably produces a Optional
(and Java’s generics does not allow to declare a type variable like
, so it is not even possible to produce an Optional
with the desired type here).
→ The solution is to provide an explicit type for the map
call:
public Optional getItems(List items){
return items.stream()
.map(s -> new B())
.findFirst();
}
Just for completeness, as said, findFirst()
is not generic and hence, can’t use the target type. Chaining a generic method allowing a type change would also fix the problem:
public Optional getItems(List items){
return items.stream()
.map(s -> new B())
.findFirst()
.map(Function.identity());
}
But I recommend using the solution of providing an explicit type for the map
invocation.