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.