首页 > 解决方案 > 在构造函数中使用多捕获异常类型

问题描述

假设我需要结合 2 个 api,它们基本上在每个方法上都会抛出异常。代码通常看起来像这样混乱:

class MultiApi{

   Api1 api1;
   Api2 api2;

   //I led the caller handle problems
   Api2Data doSomethingOrThrow() throws Api1Ex, Api2Ex {
       byte[] data = api1.getData();
       return api2.handleData(data);
   }

   //I try to recover
   Api2Data somethingElse(){

       try{ 
         byte[] data = api1.getData();
         return api2.handleData(data);
       catch (Api1Ex e){ // handle 
       } catch (Api2Ex e) //handle
       }
       // if not handled
       return null;
   }
}

所以我想我可以像这样写一个 ExceptionWrapper:

class Wrapper extends Exception{

    private Exception mTrigger;

    public Wrapper(Api1Ex e) {
        mTrigger = e;
    }

    public Wrapper(Api2Ex e) {
        mTrigger = e;
    }

    public int getCode(){
        if (mTrigger instanceof Api1Ex) {
           return ((Api1Ex)mTrigger).getCode();
        } else {
            return ((Api2Ex)mTrigger).getCode();
        }
    }
}

并像这样使用它:

   Api2Data doSomethingOrThrow() throws Wrapper {
       try{

           byte[] data = api1.getData();
           return api2.handleData(data);
       catch (Api1Ex | Api2ex e){
           throw new Wrapper(e);
       ]
   }

但是 Java 抱怨它无法解析类型。我也无法在构造函数中使用简洁的多捕获语法:

Wrapper(Api1Ex | Api2Ex e){
    mTrigger = e;
}

所以我需要写这样的东西:

Wrapper(Exception e){
    if (e instanceof Api1Ex || e instanceof Api2Ex) {
        mTrigger = e;
    } else {
       throw new RuntimeException("Got unkown Ex type!");
    }
}

从我的角度来看,这非常丑陋。这个问题有更好(意味着更优雅)的解决方案吗?我通常不感兴趣,哪个 Api 失败,只有其中一个失败,以及它抛出了哪个 errorCode(在两种情况下都具有相同的含义)

我有点想到鸭子打字功能:任何带有 a 的异常getCode就足够了。

编辑: 许多人建议最简单的方法是实现一个通用类型。但我无法以任何方式或形式修改 Api1 或 Api2。此外,getCode它不返回一个 int,而是一个包含一个 int 的枚举。枚举类型(当然)不一样,但它们的int表示是一样的。

标签: javaexceptiontype-conversionmulti-catch

解决方案


如果要使用多捕获块,则需要一个采用and的最小上限 ( LUB ) 类型的构造函数。Api1ExApi1Ex

异常参数的声明类型,表示它的类型是与替代的D1 | D2 | ... | Dn联合lub(D1, D2, ..., Dn)

https://docs.oracle.com/javase/specs/jls/se12/html/jls-14.html#jls-14.20

没有其他办法了。构造函数Wrapper(Api1Ex | Api2Ex e)永远无效。

如果您声明了几个 catch 块,重载的构造函数将起作用。

try {
    ...
} catch(Api1Ex exception) {
    throw new Wrapper(exception);
} catch (Api2Ex exception) {
    throw new Wrapper(exception);
} 

在某些时候,您需要在同时使用异常的部分和仅处理包装器异常的部分之间划清界限。那条线可能看起来很难看(就像上面的片段),但至少,它会帮助你减少所有地方的“丑陋”。


推荐阅读