在构造函数中使用多捕获异常类型

Using multi-catch Exception type in constructor

假设我需要组合 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 失败,只有其中一个失败,以及它抛出的错误代码(在两种情况下具有相同的含义)

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

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

当然这不是有效的构造函数:

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

在您的最后一个示例中,如果原始异常的类型不匹配,则创建可能触发另一个异常的异常的创建也不是正确的方法(在编译时)。
绑定构造函数参数类型的最佳方法是为两个异常使用公共基类型并将其指定为参数,例如:

private final GenericApiException genericApiException;
WrapperException(GenericApiException genericApiException){
    super(genericApiException);
    this.genericApiException = genericApiException;
}

您可以使用该构造函数:

catch (Api1Ex | Api2ex e){
  throw new WrapperException(e);
]

WrapperException 看起来比 Wrapper 更清晰。


the big but is that I cannot modify api1 or 2 in any way or form

在这种情况下,重载构造函数:

WrapperException(Api1Ex e){
    mTrigger = e;
}

WrapperException(Api2Ex e){
    mTrigger = e;
}

如果要使用 multiple-catch 块,您需要一个采用 Api1ExApi1Ex 的最小上限 (LUB) 类型的构造函数。

The declared type of an exception parameter that denotes its type as a union with alternatives D1 | D2 | ... | Dn is 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);
} 

在某些时候,您需要在同时使用异常的部分和仅处理包装器异常的部分之间划清界限。该行可能看起来很难看(如上面的代码片段),但至少,它会帮助您减少整个地方的 "ugliness"。