簡體   English   中英

正確的lambda過濾器實現

[英]Correct lambda filter implementation

我有一個需要的案例

  • map一個對象,如果映射函數拋出異常,我將其映射為null
  • filter映射的null對象流,如果為null則拋出異常,否則收集到List。

我怎么做到這一點?

list.stream().map(ob-> {
    try {
        // cannot throw only catch 
        return function(ob);
    } catch (Exception e) {
        log.error(e);
        return null;
    }            
}).filter(Objects::isNull).findFirst().orElseThrow(Exception::new);

現在我的問題是我應該如何調整/重構上面的lambda以在nullthrow new Exception()或者collect(Collectors.toList())

如果您打算報告異常(這是一個好主意),您應該首先將它映射為null 由於某些功能接口不允許拋出已檢查的異常,因此應將其重新拋出包含在未經檢查的異常中:

try {
    List<Object> result = list.stream().map(ob-> {
        try {
            // cannot throw checked exception types
            return function(ob);
        } catch(Exception e) {
            throw new CompletionException(e);
        }
    }).collect(Collectors.toList());
} catch(CompletionException ex) {
    throw (Exception)ex.getCause();
}

關鍵點在於它將拋出原始異常,其中包含所有信息,而不是通過new Exception()創建一個新實例, new Exception()根本不包含有關原因的信息。

請注意,對於某些情況,已經有專用的異常類型,例如UncheckedIOException來包裝IOException 在其他情況下,聲明自己的未經檢查的異常類型可能更清晰,以確保它不會與應用程序的其他組件拋出的其他異常混淆。

如果映射包含null鍵的非空集合,則可以按謂詞進行分區並拋出異常:

Map<Boolean, List<String>> resultMap = list.stream().map(ob-> {
    try {
        return function(ob);
    } catch (Exception e) {
        return null;
    }
}).collect(Collectors.partitioningBy(Objects::isNull));

if(!resultMap.get(Boolean.TRUE).isEmpty()) {
    throw new Exception();
}

return resultMap.get(Boolean.FALSE);

Collectors.partitioningBy(Objects::isNull)將返回一個Map<Boolean, List<T>> ,其中true將映射到一個列表,其中包含與謂詞匹配的所有元素( Objects::isNull ),並且false將映射到那些'噸。

如果true集合不為空,您知道可以引發異常。

好吧,有可能在lambdas中使用try-catch子句,但不建議這樣做,因為lambdas應盡可能短。

將映射器分隔為新方法,然后在lambda中調用它。

private static final <T, R> R tryMapOrElseNull(T t) {
    try {
        return function(t);
    } catch (Exception e) {
        this.log.error(e);
        return null;
    }
}

然后使用該方法作為Stream::map方法中的方法引用。 首先,收集新映射的元素,然后只需檢查null

newList = list.stream().map(MyClass::safeMap).collect(Collectors.toList());

if (newList.contains(null)) {
    throw new Exception();
}

如果我檢測到我不需要迭代下一個元素,我會拋出異常並立即離開流處理。 如果它無助,為什么繼續執行邏輯呢?

所以我不會在這種情況下使用內置map()而不是流。 我認為通過引入一個簡單的方法進行映射會使事情變得非常易讀:

try{
    return map(list);
} 
catch (Exception e) {
    throw new AnyExceptionYouWant(e);
}

// helper method
List<Bar> map (List<Foo> list) throws Exception{
   List<Bar>> bars = new ArrayList<>();       
   for (Foo foo : list){
          bars.add(function(foo));
   }
   return bars;
 }

如果你想使用可讀且易於維護的流,你可能不應該在function()拋出任何異常。 例如,您可以返回一個Optional列表,因此處理流中的空案例會很簡單。

我分兩步完成,先收集到一個列表:

List<T> result = list.stream().map(ob -> {
                try {
                    // cannot throw only catch, since lambda expression 
                    return function(ob);
                } catch (Exception e) {
                    log.error(e);
                    return null;
                }
            }).collect(toList()); 

其中T是映射到的元素的類型。

然后檢查無效:

if(result.contains(null)) {/* throw exeception... */}
else { /* do something else */}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM