2014-10-29 57 views
5

我有一些基於某些輸入過濾列表的Java代碼。目前,它採用的是拉姆達,例如:如何將具有動態值的lambda過濾器轉換爲方法引用

public List<ComplexObject> retrieveObjectsFilteredByTags(List<String> allowedTags) { 
    List<ComplexObject> complexObjects = retrieveAllComplexObjects(); 
    return complexObjects 
       .stream() 
       .filter(compObject -> allowedTags.contains(compObject.getTag())) 
       .collect(Collectors.toList()); 
} 

我想要做的是過濾邏輯移動到另一個方法,使其可重複使用,容易進行單元測試。所以我想用一個方法引用來代替傳遞給過濾器方法的lambda表達式。如果過濾器邏輯相當靜態(即編譯時已知允許標記的列表),很容易做到,但我無法弄清楚如何在過濾器中使用動態數據來做到這一點。

我想要的是一些方法來使用的方法引用,然後通過第二動力是什麼PARAM即

public List<ComplexObject> retrieveObjectsFilteredByTags(List<String> allowedTags) { 
    List<ComplexObject> complexObjects = retrieveAllComplexObjects(); 
    return complexObjects 
       .stream() 
       .filter(this::filterByAllowedTags, allowedTags) 
       .collect(Collectors.toList()); 
} 

所以是有可能做什麼,我想還是我可能不正確地處理這個情況呢?

回答

4

我建議傳入Predicate作爲參數。這樣,主叫方可以過濾基於它想要的任何標準,包括allowedTags或什麼:

public List<ComplexObject> retrieveObjectsFilteredBy(Predicate<ComplexObject> pred) { 
    List<ComplexObject> complexObjects = retrieveAllComplexObjects(); 
    return complexObjects.stream() 
     .filter(pred) 
     .collect(Collectors.toList()); 
} 

這被稱爲像這樣:

List<String> allowedTags = ... ; 
    List<ComplexObject> result = 
     retrieveObjectsFilteredBy(cobj -> allowedTags.contains(cobj.getTag())); 

但是你可以走得更遠,取決於有多少重構你願意做的事情。而不是「檢索」返回List,如何返回Stream?而不是檢索過濾器方法返回List,如何返回Stream呢?

public Stream<ComplexObject> retrieveObjectsFilteredBy2(Predicate<ComplexObject> pred) { 
    Stream<ComplexObject> complexObjects = retrieveAllComplexObjects2(); 
    return complexObjects.filter(pred); 
} 

和主叫方是這樣的:

List<String> allowedTags = ... ; 
    List<ComplexObject> result = 
     retrieveObjectsFilteredBy2(cobj -> allowedTags.contains(cobj.getTag())) 
      .collect(toList()); 

現在,如果你在它仔細看,你可以看到,檢索,過濾方法不增加任何價值可言,所以你還不如內聯成來電:

List<String> allowedTags = ... ; 
    List<ComplexObject> result = 
     retrieveAllComplexObjects2() 
      .filter(cobj -> allowedTags.contains(cobj.getTag())) 
      .collect(toList()); 

當然,根據呼叫者想要做什麼,它可能不想收集結果到一個列表;它可能想要用forEach()或其他方法來處理結果。

現在,你仍然可以分解出過濾器進入它自己的方法,用於測試/調試,你可以使用的方法參考:

boolean cobjFilter(ComplexObject cobj) { 
    List<String> allowedTags = ... ; 
    return allowedTags.contains(cobj.getTag()); 
} 

    List<ComplexObject> result = 
     retrieveAllComplexObjects2() 
      .filter(this::cobjFilter) 
      .collect(toList()); 

如果您不希望過濾器以具有合法的標籤內置到它,你可以從一個斷言它變成一個返回,而不是一個謂語一個高階函數:

Predicate<ComplexObject> cobjFilter(List<String> allowedTags) { 
    return cobj -> allowedTags.contains(cobj.getTag()); 
} 

    List<String> allowedTags = ... ; 
    List<ComplexObject> result = 
     retrieveAllComplexObjects2() 
      .filter(cobjFilter(allowedTags)) 
      .collect(toList()); 

哪個這些變化是最有意義的取決於你的應用是什麼樣的,什麼樣的過濾時需要的動態性。

+0

很多很好的選擇在這裏。很好的答案。 – 2014-10-30 06:36:18

2

以下情況如何?它使用單獨的方法提取謂詞,使其易於測試,並且可以輕鬆重用。

public Predicate<ComplexObject> tagAllowed(List<String> allowedTags) { 
    return (ComplexObject co) -> allowedTags.contains(co.getTag()); 
} 

public List<ComplexObject> retrieveObjectsFilteredByTags(List<String> allowedTags) { 
    List<ComplexObject> complexObjects = retrieveAllComplexObjects(); 
    return complexObjects 
       .stream() 
       .filter(tagAllowed(allowedTags)) 
       .collect(Collectors.toList()); 
} 
2

與方法參考this::filterByAllowedTags的問題是,它具有形狀:

(ComplexObject, List<String>) -> boolean 

但它被傳遞到filter()其中期望形狀的拉姆達:

(ComplexObject) -> boolean 

換句話說, this::filterByAllowedTags永遠不能是Predicate,但它可能是一些備用接口Predicate2。那麼你還需要一個過濾器的過載,需要Predicate2

Eclipse Collections(原GS類別)具有的行爲就像filter()方法select(),和selectWith(),其行爲類似於我剛纔所描述的過載。

使用select()

public List<ComplexObject> retrieveObjectsFilteredByTags(List<String> allowedTags) 
{ 
    // retrieveAllComplexObjects returns a MutableList, possibly FastList 
    MutableList<ComplexObject> complexObjects = retrieveAllComplexObjects(); 
    // select() returns MutableList here which extends List 
    return complexObjects.select(compObject -> allowedTags.contains(compObject.getTag())); 
} 

使用selectWith()

public List<ComplexObject> retrieveObjectsFilteredByTags(List<String> allowedTags) 
{ 
    // retrieveAllComplexObjects returns a MutableList, possibly FastList 
    MutableList<ComplexObject> complexObjects = retrieveAllComplexObjects(); 
    // select() returns MutableList here which extends List 
    return complexObjects.selectWith(this::filterByAllowedTags, allowedTags); 
} 

private boolean filterByAllowedTags(ComplexObject complexObject, List<String> allowedTags) 
{ 
    return allowedTags.contains(complexObject.getTag()); 
} 

注:我的Eclipse集合的參與者。

相關問題