如何捕获多个异常,然后将它们传递给特定的处理程序?



>我在Java 8应用程序中有一些代码,它抛出了N个来自不同库的不同异常类。目前每个异常类都有一个单独的处理程序,尽管它们共享一些通用代码。我想重构它以避免:

  1. 重复具有某些共性(例如使用开关、instanceof或强制转换(的异常类列表
  2. 重复调用someCommonCodeN 次
class MyClass {
public void errorHandler(FirstException e) {
System.out.println("This error is not so bad");
}
public void errorHandler(SecondException e) {
System.out.println("This error is worse");
}
public void someMethod() {
try {
riskItAll();
} catch(FirstException | SecondException e) {
someCommonCode();
errorHandler(e);
moreCommonCode();
} catch(Exception e) {
uncommonCode();
}
}
}

到目前为止,我一直在尝试查找以这种方式处理多个捕获块的文档/示例,因为我还没有找到用于描述此类块中e类型的术语。http://www.oracle.com/technetwork/articles/java/java7exceptions-486908.html 上没有引入可搜索的术语

它可能是一个泛型,但这将是令人惊讶的,因为您无法捕获类型参数的实例。

上面的代码片段没有构建 -errorHandler时引发的编译器错误是

error: no suitable method found for errorHandler(RuntimeException)
method MyClass.errorHandler(FirstException) is not applicable (argument mismatch; RuntimeException cannot be converted to FirstException)
method MyClass.errorHandler(SecondException) is not applicable (argument mismatch; RuntimeException cannot be converted to SecondException)

重载方法不起作用。FirstException | SecondException e是一种联合类型,这是 Java 中的一种特殊情况,仅适用于多捕获异常变量。当您尝试将其传递给方法时,编译器会将其视为联合中所有类型的最小上限。

但是,您可以重新引发联合类型异常并捕获其组件类型:

public void someMethod() {
try {
riskItAll();
} catch (FirstException | SecondException e) {
someCommonCode();
try {
throw e;
} catch (FirstException e2) {
System.out.println("This error is not so bad");
} catch (SecondException e2) {
System.out.println("This error is worse");
}
moreCommonCode();
} catch (Exception e) {
uncommonCode();
}
}

但这比instanceof更丑陋:

public void someMethod() {
try {
riskItAll();
} catch (FirstException | SecondException e) {
someCommonCode();
if (e instanceof FirstException) {
System.out.println("This error is not so bad");
} else {
System.out.println("This error is worse");
}
moreCommonCode();
} catch (Exception e) {
uncommonCode();
}
}

真的没有比这更好的方法了。多重捕获块旨在以完全相同的方式处理多个异常,而不是用于简化部分重复的代码。

我们有一个(有点丑陋的(解决方案:

try {
... whatever
} catch (SomeBaseException e) {
new Handler().handle(e);
}

其中handle()只是进行一连串的if instanceof调用。

不幸的是,这很快就会变得非常复杂 - 所以有一个重要的方面:每当我们触摸handle()- 我们首先进入相应的单元测试并为我们必须添加的额外处理添加一个测试用例。在这种情况下,TDD(测试驱动开发(不仅"有帮助",而且是强制性的。

它并不漂亮,但这是多个调度错误处理的工作实现,不依赖于修补错误类本身或特定基本错误类的能力。

既没有重复异常名称,也没有重复常见的代码调用,但由于任何其他原因,这可能是一个糟糕的动态语言用户的解决方案。

import java.lang.reflect.Method;
import java.util.HashMap;
import java.lang.reflect.InvocationTargetException;
class FirstException extends RuntimeException {
}
class SecondException extends Exception {
}
public class MyClass {
public void errorHandler(FirstException e) {
System.out.println("This error is not so bad");
}
public void errorHandler(SecondException e) {
System.out.println("This error is worse");
}
public void someCommonCode() {
System.out.println("Here's one of the usual errors:");
}
public void moreCommonCode() {
System.out.println("That's it.n");
}
public void uncommonCode() {
System.out.println("Surprise!n");
}
public static final HashMap<Class, Method> errorMap;
static {
HashMap<Class, Method> errMap = new HashMap<Class, Method>();
for (Method method: MyClass.class.getMethods()) {
String name = method.getName();
Class[] parameters = method.getParameterTypes();
if (parameters.length == 0 || !name.equals("errorHandler")) {
continue;
}
Class parameter1 = parameters[0];
if (method.getParameterCount() == 1 && Exception.class.isAssignableFrom(parameter1)) {
errMap.put(parameter1, method);
}
}
errorMap = errMap;
}
public void riskItAll(int risk) throws Exception {
if (risk == 0) {
throw new FirstException();
} else if (risk == 1) {
throw new SecondException();
} else {
throw new Exception();
}
}
public void someMethod(int risk) {
try {
riskItAll(risk);
} catch(Exception e) {
Class errorClass = e.getClass();
if (errorMap.containsKey(errorClass)) {
someCommonCode();
try {
errorMap.get(errorClass).invoke(this, e);
} catch (IllegalAccessException | InvocationTargetException reflectionError) {
System.out.println("¯\_(ツ)_/¯");
}
moreCommonCode();
} else {
uncommonCode();
}
}
}
public static void main(String[] args) {
MyClass instance = new MyClass();
instance.someMethod(0);
instance.someMethod(1);
instance.someMethod(2);
}
}

相关内容

最新更新