I have a relatively simple looking problem that I am trying to solve. There doesn't seem to be an intuitive way to do this or, I am missing something here.
Consider this method to find the main image and if none exists, return first image-
public Image findMainImage(Collection<? extends Image> images) {
if (images == null || images.isEmpty()) return null
return images.stream()
.filter(Image::isMain)
.findFirst()
.orElse(images.iterator().next())
}
I get an error - orElse(capture<? extends Image>) in Optional cannot be applied
Any direction on this would be great.
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.
Suppose you have a List<? extends Number>
. You could not add any number to this list, because it could be a List<Integer>
and the number you are attempting to add could be a Float
.
For the same reason, the method orElse(T t)
of Optional<T>
requires a T
. Since the Optional
in question is an Optional<? extends Image>
, the compiler can't be sure that images.iterator().next()
is of the right type.
I got this to compile by putting in .map(t -> (Image) t)
:
return images.stream()
.filter(Image::isMain)
.findFirst()
.map(t -> (Image) t)
.orElse(images.iterator().next());
In fact, for some reason I can't understand, it works even without the cast. Just using
.map(t -> t)
seems to make this work.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With