我在尝试Java 8的Lambda表达式时有一个问题。
通常它工作得很好,但现在我有了抛出IOException的方法。
最好看看下面的代码:
class Bank{
....
public Set<String> getActiveAccountNumbers() throws IOException {
Stream<Account> s = accounts.values().stream();
s = s.filter(a -> a.isActive());
Stream<String> ss = s.map(a -> a.getNumber());
return ss.collect(Collectors.toSet());
}
....
}
interface Account{
....
boolean isActive() throws IOException;
String getNumber() throws IOException;
....
}
问题是,它不能编译,因为我必须捕获isActive-和getNumber-Methods的可能异常。但是,即使我显式地使用如下所示的try-catch-Block,它仍然不能编译,因为我没有捕获异常。所以,要么是JDK有bug,要么是我不知道如何捕捉这些异常。
class Bank{
....
//Doesn't compile either
public Set<String> getActiveAccountNumbers() throws IOException {
try{
Stream<Account> s = accounts.values().stream();
s = s.filter(a -> a.isActive());
Stream<String> ss = s.map(a -> a.getNumber());
return ss.collect(Collectors.toSet());
}catch(IOException ex){
}
}
....
}
我怎样才能让它工作呢?谁能给我点提示吗?
Java中的功能接口不声明任何已检查或未检查的异常。
我们需要改变方法的签名:
boolean isActive() throws IOException;
String getNumber() throwsIOException;
To:
boolean isActive();
String getNumber();
或者用try-catch block处理它:
public Set<String> getActiveAccountNumbers() {
Stream<Account> s = accounts.values().stream();
s = s.filter(a ->
try{
a.isActive();
}catch(IOException e){
throw new RuntimeException(e);
}
);
Stream<String> ss = s.map(a ->
try{
a.getNumber();
}catch(IOException e){
throw new RuntimeException(e);
}
);
return ss.collect(Collectors.toSet());
}
另一种选择是编写自定义包装器或使用像ThrowingFunction这样的库。
使用库,我们只需要将依赖项添加到pom.xml:
<dependency>
<groupId>pl.touk</groupId>
<artifactId>throwing-function</artifactId>
<version>1.3</version>
</dependency>
并使用特定的类,如ThrowingFunction, ThrowingConsumer, ThrowingPredicate, ThrowingRunnable, ThrowingSupplier。
代码的最后是这样的:
public Set<String> getActiveAccountNumbers() {
return accounts.values().stream()
.filter(ThrowingPredicate.unchecked(Account::isActive))
.map(ThrowingFunction.unchecked(Account::getNumber))
.collect(Collectors.toSet());
}
为了正确地添加IOException(到RuntimeException)处理代码,你的方法看起来像这样:
Stream<Account> s = accounts.values().stream();
s = s.filter(a -> { try { return a.isActive(); }
catch (IOException e) { throw new RuntimeException(e); }});
Stream<String> ss = s.map(a -> { try { return a.getNumber() }
catch (IOException e) { throw new RuntimeException(e); }});
return ss.collect(Collectors.toSet());
现在的问题是,IOException必须被捕获为RuntimeException并转换回IOException——这将向上述方法添加更多代码。
为什么要使用Stream,因为它可以这样做——而且该方法会抛出IOException,所以也不需要额外的代码:
Set<String> set = new HashSet<>();
for(Account a: accounts.values()){
if(a.isActive()){
set.add(a.getNumber());
}
}
return set;