很抱歉 TL;DR,但我觉得它需要一些解释,否则会被误解。
我有一个方法可以调用(通常是外部的)代码,我希望有时会抛出 RuntimeException,并使用可以抛出 InterruptedException 或 ExecutionException 的期货,并且我希望能够从调用直到抛出异常,以及抛出的异常。我写了一些有用的东西,但不幸的是,代码看起来让我觉得我做错了什么。我认为我真正想要的是 multi-catch 成为一个更通用的概念。这将允许非常干净的代码来解决它,有点像这样:
public class SomeResults {
private final Set<SomeReturnType> valuesReturned;
private final @Nullable RuntimeException | ExecutionException | InterruptedException exception;
public SomeResults(Set<SomeReturnType> valuesReturned, RuntimeException | ExecutionException exception {
this.valuesReturned = valuesReturned;
this.exception = exception;
}
public Set<SomeReturnType> getValuesReturned() {
return valuesReturned;
}
public @Nullable RuntimeException | ExecutionException | InterruptedException getException();
}
并有一个方法可以结束对外部代码的调用......
generateResults(Bar bar) {
// Setup code
Set<SomeReturnType> valuesReturned = new LinkedHashSet<>();
...
// loop
{
// stuff
... // exceptions in this method should throw except for this one external code call
try {
valuesReturned.add(externalCodeCallGetSomeReturnValue(bar))
}
catch( RuntimeException | ExecutionException | InterruptedException e) {
return new MyResults(valuesReturned, e)
}
...
}
return new MyResults(valuesReturned, (RuntimeException | ExecutionException | InterruptedException) null);
}
随后做
SomeResults myResults = foo.generateResults(new Bar());
if(myResults.getException() != null) {
throw(myResults.getException);
}
等等。请注意,我确实要注意总是想立即重新抛出异常——这取决于谁在使用这些结果,他们想用它们做什么。我可能会做类似的事情
try {
SomeResults myResults = foo.generateResults(new Bar());
Foobar Foobar = new Foobar(myResults);
}
catch(Exception e) {
// I don't want to see any exceptions from externalCodeCallGetSomeReturnValue(bar) here
...
}
当然,我可以让生成结果的函数中抛出异常,而不是捕获异常并将其作为结果返回。这有两个相当大的问题: 1. 现在返回一组值会很尴尬——我也许可以将一个 Set 传递给需要“返回”结果的方法,它会修改该集合而不是返回一个集合。这允许集合在返回异常时是最新的。例如
generateResults(Bar bar, Set<SomeReturnType> orderedListForMeToWrite) throws ExecutionException, InterruptedException
- 如果围绕外部方法调用的代码抛出运行时异常怎么办?现在我没有简单的方法来区分异常调用是来自对外部代码的实际调用,还是其他什么!我在尝试这种设计时实际上遇到了这个问题。该代码从其他地方抛出了 IllegalArgumentException,我的代码处理将其视为是从 SomeReturnType externalCodeCallGetSomeReturnValue(Bar bar) 中抛出的。这似乎是一个代码健康问题,这就是我放弃这个解决方案的原因。
我采用的解决方案是将异常存储为异常。但是,我讨厌丢失那种类型的信息。没有额外的代码工作,如果有东西想抛出它,它必须声明“抛出异常”,这不好,类似的代码健康问题。有没有好办法来处理这种情况?我最终要让它按照我想要的方式工作如下:
public static class SomeResults {
private final Set<SomeReturnType> orderedReturnValues;
private final @Nullable Exception exception;
AsyncEchoesResult(Set<SomeReturnType> responses) {
this.orderedResponses = responses;
this.exception = null;
}
AsyncEchoesResult(Set<SomeReturnType> responses, RuntimeException exception) {
this.orderedResponses = responses;
this.exception = exception;
}
AsyncEchoesResult(Set<SomeReturnType> responses, ExecutionException exception) {
this.orderedResponses = responses;
this.exception = exception;
}
AsyncEchoesResult(Set<SomeReturnType> responses, InterruptedException exception) {
this.orderedResponses = responses;
this.exception = exception;
}
public Set<SomeReturnType> getResponses() {
return orderedResponses;
}
public @Nullable Exception getException() {
return exception;
}
public void throwExceptionIfExists() throws ExecutionException, InterruptedException {
try {
throw (exception);
}
catch (RuntimeException | ExecutionException | InterruptedException e) {
throw e;
}
catch (Exception e) {
throw new RuntimeException("Unexpected exception type in SomeResults",e);
}
}
}
显然,这很丑陋。如果我讨厌构造函数,我可以很容易地用一个接受异常的构造函数替换它们,但这会将类型检查削弱为仅运行时调用 throwException()。无论如何,有没有更好的替代品?请注意,我使用的是 JDK 7,所以虽然 JDK 8 的答案会很有趣,但这并不能解决我正在处理的问题。