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.