One way to fix it is to use a type parameter:
public <I extends Image> I findMainImage(Collection<I> images) {
if (images == null || images.isEmpty()) return null;
return images.stream()
.filter(Image::isMain)
.findFirst()
.orElse(images.iterator().next());
}
Because then (to the compiler) the Optional
definitely has the same type argument as images
.
And we could use that as a capturing helper if we wanted:
public Image findMainImage(Collection<? extends Image> images) {
return findMainImageHelper( images );
}
private <I extends Image> I findMainImageHelper(Collection<I> images) {
// ...
}
Personally, I would just use the generic version because then you can do e.g.:
List<ImageSub> list = ...;
ImageSub main = findMainImage( list );
Basically...the reasoning for why it doesn't compile originally is to keep you from doing something like this:
public Image findMainImage(
Collection<? extends Image> images1,
Collection<? extends Image> images2
) {
return images1.stream()
.filter(Image::isMain)
.findFirst()
.orElse(images2.iterator().next());
}
And in the original example the compiler doesn't need to determine the fact that both the Stream
and the Iterator
come from the same object. Two separate expressions that reference the same object get captured to two separate types.
Collection<Image>
, but the way generics work,Optional<T>.orElse
can only accept aT
. – Moslempublic<T extends Image> T findMainImage(Collection<T> images)
– Emmerie? extends ...
), without binding it to a definitive type (e.g. by assigning it to a variable of the typeImage
, you'll have a? extends Image
type, where as theorElse
method expects anImage
because of type inference. Java generics are a pain, especially when wildcards are involved. – Bowens? extends Image
which parametrizes the stream is the same? extends Image
that comes out ofimages.iterator().next()
but the compiler doesn't know. By altering the signature and giving a name to collection parameter you can then prove to the compiler that it's the same type in both places. – Emmerie