Search code examples
javaexceptionmulti-catch

reference type of exception issue in multi-catch block


e is of type Exception but prints Exception1 in below code:

class Exception1 extends IOException {void info(){}}
class Exception2 extends Exception {}

class TestMultiCatch {
    public static void main(String args[]) {
        try {
            int a = 10;
            if (a <= 10)
                throw new Exception1();
            else
                throw new Exception2(); 
        } catch (Exception1 | Exception2 e) {
            e.info();  //line 1 error "The method info() is undefined for type Exception"
            System.out.println(e);   //prints Exception1 (after commenting line 1)
        }
    }
}

From what I studied "e" should be of type Exception which is common base class of Exception1 and Exception2. Which it is, as evident from message in line 1.

But then why:

System.out.println(e); //prints Exception1 and not Exception
System.out.println(e instanceof IOException); //prints true and not false
System.out.println(e instanceof Exception1); //prints true and not false
System.out.println(e instanceof Exception2); //false

? Thanks.


Solution

  • When you use a multi-catch clause (the Exception1 | Exception2 e form of catch), the compile-time type of e is the greatest type that the two types have in common, since of course the code has to handle either type of exception.From the spec:

    An exception parameter may denote its type as either a single class type or a union of two or more class types (called alternatives). The alternatives of a union are syntactically separated by |.

    A catch clause whose exception parameter is denoted as a single class type is called a uni-catch clause.

    A catch clause whose exception parameter is denoted as a union of types is called a multi-catch clause.

    ...

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

    ...where lub is Least Upper Bound as defined here.

    If you want to use anything that's specific to Exception1 or Exception2, use separate catch blocks:

    } catch (Exception1 e) {
        // Something using features of Exception1
    } catch (Exception2 e) {
        // Something using features of Exception2
    }
    

    If info exists on both Exception1 and Exception2, refactor them so that info exists on a common ancestor class of them:

    class TheAncestorException extends Exception {
        public void info() { // Or possibly make it abstract
            // ...
        }
    }
    class Exception1 extends TheAncestorException {
        // Optionally override `info` here
    }
    class Exception2 extends TheAncestorException {
        // Optionally override `info` here
    }
    

    ...so the compiler can give e the type TheAncestorException and make info accessible.