2015-04-24 200 views
5

我有一个相对简单的寻找问题,我试图解决。似乎没有一种直观的方式来做到这一点,或者我在这里错过了一些东西。Java 8泛型集合与可选项

考虑此方法来查找主图像,如果不存在,返回第一个图像 -

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()) 
} 

我得到一个错误 - orElse(capture<? extends Image>) in Optional cannot be applied

任何方向上,这将是巨大的。

+3

这可能会工作,如果它只是一个'集合',但泛型的工作方式,'可选。或Else'只能接受'T'。 –

+0

您可以将方法签名更改为'public T findMainImage(Collection images)' – Misha

+0

@misha我通过更改签名来了解它的工作原理,我更感兴趣的是理解此背后的推理。 – sanz

回答

7

一个解决它的方法是使用一种类型的参数:

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()); 
} 

因为然后(编译器)的绝对Optional具有相同类型参数作为images

而且我们可以使用,作为一个capturing helper如果我们想:

public Image findMainImage(Collection<? extends Image> images) { 
    return findMainImageHelper(images); 
} 

private <I extends Image> I findMainImageHelper(Collection<I> images) { 
    // ... 
} 

就个人而言,我只是将使用通用版本,因为那么你可以做如:

List<ImageSub> list = ...; 
ImageSub main = findMainImage(list); 

基本上......为什么最初不编译的原因是为了避免你这样做:

public Image findMainImage(
    Collection<? extends Image> images1, 
    Collection<? extends Image> images2 
) { 
    return images1.stream() 
        .filter(Image::isMain) 
        .findFirst() 
        .orElse(images2.iterator().next()); 
}

而在原始示例中,编译器不需要确定事实:StreamIterator来自同一个对象。引用相同对象的两个单独的表达式被捕获到两个单独的类型。

+1

或者,您可以使用Collections.unmodifiableCollection(images).stream()。 ...' – Holger

2

假设您有List<? extends Number>。您无法将任何号码添加到此列表中,因为它可能是List<Integer>,您尝试添加的号码可能是Float

出于同样的原因,Optional<T>的方法orElse(T t)需要T。由于OptionalOptional<? extends Image>,编译器不能确定images.iterator().next()是正确的类型。

我得到这个由投入.map(t -> (Image) t)编译:

return images.stream() 
      .filter(Image::isMain) 
      .findFirst() 
      .map(t -> (Image) t) 
      .orElse(images.iterator().next()); 

事实上,由于某种原因,我无法理解,它的工作原理,即使没有投。正在使用

.map(t -> t) 

似乎使这项工作。

+1

我假设你在'findFirst'之前确实拥有'.map','t'类型是集合的上界,'Image' ,所以无论你是否将't'转换为'Image'或不',它都是'Image',并且'Stream'返回ed现在是'Stream ',而不是'Stream <?扩展图像>'。 'Optional'现在是'Optional '而不是'Optional <?扩展Image>',并允许您将'Image'传递给'orElse'。 – rgettman

+0

@rgettman'map'可以在任何位置。 'Optional'也有一个'map'方法,所以我映射了一个'Optional <?将Image>'扩展为'Optional '。 –

+0

我甚至没有意识到'Optional'也有'map'方法。有句话说“你每天都会学到新东西”,而且我刚刚学到了一些新东西。 – rgettman