Reputation: 2950
The following classes and methods:
class A<T extends B> { }
class B {}
Stream<A<? extends B>> find() {
return findAll() // Stream<Optional<A<? extends B>>>
.filter(Optional::isPresent) // Stream<Optional<A<? extends B>>>
.map(Optional::get) // Stream<A<capture of ? extends B>>
.filter(a -> false); // Stream<A<capture of ? extends B>>
}
Stream<Optional<A<? extends B>>> findAll() {
return Stream.empty();
}
Compile fine with javac, but lead to type errors in IDEA:
The errors disappear when I either
filter(Optional::isPresent()).map(Optional::get)
pairfilter
callI can't make any sense of that. Is it an IDEA bug?
Edit: Some new insights thanks to LuCio :
While my version of javac (10.0.2) does not complain, other javac versions and IDEs do, so it's not an IDEA bug
Splitting the expression in two and giving the intermediate value an explicit type helps:
Stream<A<? extends B>> aStream = findAll()
.filter(Optional::isPresent)
.map(Optional::get);
return aStream
.filter(a -> false);
(note that the type IDEA infers for aStream
is Stream<? extends A<? extends B>>
, although it just accepts the assignment to this type)
So the question becomes: Why is the intermediate type the way it is, and why is it apparently OK to just ignore it by assigning to another type?
Upvotes: 2
Views: 137
Reputation: 2153
It's because Stream.map
has the following signature:
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
In this case, R
is A<? extends B>
. Hence, the return value of the function is implicitly
? extends A<? extends B>
At which point, I believe this question is to an extent answered by
Is List<Dog> a subclass of List<Animal>? Why are Java generics not implicitly polymorphic?
This can be made to compile nicely by either changing the return type:
<T extends B> Stream<? extends A<? extends B>> find() {
return findAll() // Stream<Optional<A<? extends B>>>
.map(Optional::get) // Stream<A<capture of ? extends B>>
.filter(a -> false); // Stream<A<capture of ? extends B>>
}
or explicitly casting the function to return A<? extends B>
:
<T extends B> Stream<A<? extends B>> find() {
return findAll()
.map((Function<Optional<A<? extends B>>, A<? extends B>>) Optional::get)
.filter(a -> false);
}
To clarify, a Stream<C>
, where C extends A<B>
, is not itself a Stream<A<? extends B>>
.
Upvotes: 2