简体   繁体   中英

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.

If you want to employ the multiple-catch block, you need a constructor that takes the least upper bound ( LUB ) type of Api1Ex and Api1Ex .

The declared type of an exception parameter that denotes its type as a union with alternatives D1 | D2 | ... | Dn D1 | D2 | ... | Dn D1 | D2 | ... | Dn is lub(D1, D2, ..., Dn) .

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

There is no way around it. The constructor Wrapper(Api1Ex | Api2Ex e) is never valid.

Overloaded constructors will work if you declare a couple of catch blocks.

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

At some point, you will need to draw a clear line between the part that uses both exceptions and the part that only deals with your wrapper exception. That line may look ugly (like the snippet above), but, at least, it will help you reduce "ugliness" all over the place.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM