2015-04-24 104 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