2

I've a custom exception with few attributes that inherits from Exception class.

Depending on instance of Exception I would like to return a code. Code:

public int manageException(Exception exception) {               
    int code = 0;               

    if (exception instanceof MyCustomException) {       
        code = ((MyCustomException) exception).getCode();       
    } else if (exception instanceof NestedRuntimeException) {       
        code = 444;         
    } else if (exception instanceof HibernateException) {       
        code = 555;         
    } else {        
        code = 666;
    }       

    return code;
}
DJClayworth
  • 26,349
  • 9
  • 53
  • 79
user1750751
  • 275
  • 3
  • 15
  • 3
    If you put this in multiple `catch` clauses of `try-catch` block, you won't have to use `instanceof`. – Joffrey Jul 15 '15 at 12:59
  • I implement this method because I should to send a mail with the error. So, I want to manage the exceptions in only one place. – user1750751 Jul 15 '15 at 13:03
  • I assume you're using Java 8? – T.J. Crowder Jul 15 '15 at 13:04
  • 1
    There's a convoluted way with lambdas (but not in Java 7), but I don't know that I'd say it was better than `instanceof`. This may be one of those rare occasions when it's the right tool for the job, provided you really need to handle these exceptions in more than one place. Are you sure you're not handling the exceptions too early/deep? – T.J. Crowder Jul 15 '15 at 13:07
  • Yes, I think the same. I'm only curious. – user1750751 Jul 15 '15 at 13:09

3 Answers3

2

You can use overloading like this:

public int manageException(MyCustomException e) {
    return e.getCode();
}

public int manageException(NestedRuntimeException e) {
    return 444;
}

public int manageException(HibernateExceptionexception e) {
    return 555;
}

public int manageException(Exception e) {
    return 666;
}

Edit after comment from @T.J. Crowder:

Keep in mind that you will still need multiple catch blocks in order to call the correct method. The overload is based on the compile-time type of the exception. Simply doing catch (Exception e) { int code = this.manageException(ex); } will always return 666.

marstran
  • 26,413
  • 5
  • 61
  • 67
  • 3
    @user1750751: This still means multiple `catch` blocks, as the overload is chosen at *compile-time* based on the declared type of what you pass in, so I'm not sure it's buying you anything. Just `catch (Exception ex) { int code = this.manageException(ex); }` will always return `666`. – T.J. Crowder Jul 15 '15 at 13:14
  • oh, ok. I will check. – user1750751 Jul 15 '15 at 13:20
  • 1
    Upvoters: I don't think you understand the question. This answer doesn't help anything. Compare: http://pastie.org/10294519 with http://pastie.org/10294517 – T.J. Crowder Jul 15 '15 at 13:25
  • Good point @T.J.Crowder. I agree. I think I still answered the question though, as I am not using instanceof. :) – marstran Jul 15 '15 at 13:31
  • Yes, you did answer -- not *usefully*, however. :-) And not calling out the major problem with this was probably worse than not raising it at all. – T.J. Crowder Jul 15 '15 at 13:35
  • 3
    @T.J.Crowder is right, I don't think this method will be of any use actually, as you probably won't know the actual type of the exception. Therefore, to call these methods, the OP will have to use a multi catch or an instanceof and a cast, which is what he's trying to avoid in the first place – Joffrey Jul 15 '15 at 13:35
2

If

  • You need to handle these exceptions in multiple locations, and

  • You don't want multiple catch blocks (one for each exception type) in each location

...then instanceof is about as clean as you're likely to get in Java 7.

Having said that, though, you could do this:

public void manageException(Runnable r) {
    try {
        r.run();
    }
    catch (NestedRuntimeException nre) {
        throw new MyCustomException(444, nre);
    }
    catch (HibernateException he) {
        throw new MyCustomException(555, he);
    }
    catch (Exception e) {
        throw new MyCustomException(666, e);
    }
}

...and then everywhere you need it:

try {
    this.manageException(new Runnable() {
        @Override
        public void run() {
            // Do something
        }
    });
}
catch (MyCustomException mce) {
    int code = mce.getCode();
}

...but it's not buying you much and it's really ugly. :-)

In Java 8, it's a lot cleaner. manageException is the same, but the calls are just:

try {
    this.manageException(() => {
        // Do something here
    });
}
catch (MyCustomException mce) {
    int code = mce.getCode();
}

For me, the Java 8 version nearly starts winning over instanceof. The Java 7 version, not so much.

(Why Runnable in the above? Because the JDK authors decided not to define a new standard functional interface that accepts no arguments and has no return value; more in this question. They generalized the concept of Runnable instead. If the semantics bother you (they would me), you can define your own.)

Community
  • 1
  • 1
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
1

If you want to map the type of exception to different error codes, you could use a map:

Map<Class<? extends Exception>, Integer> map = new HashMap<> ();
map.put (Exception.class, 5);
map.put (NullPointerException.class, 42);

try {
  throw null; //throws NPE
} catch (Exception e) {
  System.out.println (map.get (e.getClass ())); //output is 42
}

I think this would be easy expendable, as you could read the mapping from a config file instead of hard coding it, so you could add other exception and error codes without making changes to your code.

You have to test the return value of map.get() for null, as it could be an Exception you didn't specify before so there is no Integer mapped to it.

Attention: As mentioned in the first comment, this would only work if you want to have an exact mapping of Classes to error codes. If Subclasses of an exception should have the same error code as their super class, this solution won't work without modification.

Alex
  • 1,175
  • 1
  • 8
  • 25
  • Problem is that if the declared type of the exception in a public API is `FooException` but what's actually thrown is an internal `FooSubclassException`, your mapping will fail, even though as far as you know, it's a `FooException`. In general, you can't rely on equating class instances. – T.J. Crowder Jul 15 '15 at 13:27
  • That's right, I didn't think of that. A possible solution would be to write an container which looks into the map, and if there is no mapping found. invokes clazz.isAssignableFrom(exceptionClazz), for each of the Maps entries. Then, if there were more than one possible classes, you would have to decide which to use (probably the most specialized one). – Alex Jul 15 '15 at 13:41