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

时间:2017-07-18 19:22:44

标签: java exception-handling

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

  1. 重复具有某些共性的异常类列表(例如使用开关,instanceof或强制转换)
  2. 重复拨打someCommonCode N次
  3. 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();
        }
      }
    }
    

    到目前为止,我一直试图找到以这种方式处理多个catch块的文档/示例,因为我还没有找到用于描述这样一个块内e类型的术语。 http://www.oracle.com/technetwork/articles/java/java7exceptions-486908.html

    上没有引入可搜索的术语

    它可能是通用的,但这会令人惊讶,因为你不能catch an instance of a type parameter

    上面的代码片段没有构建 - 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)
    

3 个答案:

答案 0 :(得分:1)

我们有一个(某种程度上难看的)解决方案:

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

其中handle()只是进行if instanceof次级联调用。

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

答案 1 :(得分:1)

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

但是,你可以重新抛出一个union-type异常并捕获它的组件类型:

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();
    }
}

实际上没有更好的方法。多捕获块设计用于以完全相同的方式处理多个异常,而不是简化部分重复的代码。

答案 2 :(得分:0)

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

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

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);
  }
}