我有一个需要的案例
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);
现在我的问题是我应该如何在throw new Exception()
或null
上调整/重构上述lambda到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()
创建一个根本不包含原因信息的新实例。
请注意,对于某些情况,已经存在专用的异常类型,例如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
集合不为空,您知道可以引发异常。
如果我检测到我不需要迭代下一个元素,我会抛出异常并立即离开流处理。如果它无助,为什么继续执行逻辑呢?
所以我不会在这种情况下使用内置的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
s列表,因此处理流中的空案例会很简单。
好吧,有可能在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();
}
我分两步完成,先收集到一个列表:
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 */}