Search code examples
javaexceptiontype-conversionmulti-catch

Using multi-catch Exception type in constructor


Suppose I need to combine 2 apis which throw Exceptions on basically every method. The code usually looks cluttered like this:

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

So I thought I could write an ExceptionWrapper like this:

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

and use it like this:

   Api2Data doSomethingOrThrow() throws Wrapper {
       try{

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

But Java is complaining that it cannot resolve the type. I'm also not able to use the neat multi-catch syntax in my Constructor:

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

So I need to Write something like this:

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

Which is very ugly from my point of view. Is there a better (meaning more elegant) solution to this problem? I usually am not interested, which Api Fails, Only that one of them does, and which errorCode it threw (which have the same meaning in both cases)

I kinda think of a duck-typing feature: Any Exception with a getCode would be sufficient.

EDIT: as many suggest the easiest way would be to implement a common type. But I am not able to modify Api1 or Api2 in any way or form. Also the getCode doesn't return an int, but an enum holding one. The enum types are (of course) not the same but their int representation is.


Solution

  • Of course that is not a valid constructor :

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

    In your last sample, the creation of an exception that may trigger the throw of another exception if the type of the original exception doesn't match is not the correct way either (while it compiles).
    The best way to bound the parameter type of the constructor is using a common base type for the both exceptions and specify it as parameter such as :

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

    You can so use that constructor :

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

    WrapperException looks clearer than Wrapper.


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

    In this case, overload the constructor :

    WrapperException(Api1Ex e){
        mTrigger = e;
    }
    
    WrapperException(Api2Ex e){
        mTrigger = e;
    }