TL;DR: The static type is the nearest common super class.
The Java Language Specification, section 14.20. The try
statement, says:
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 with a single class type is that class type.
The declared type of an exception parameter that denotes its type as a union with alternatives D1 | D2 | ... | Dn is lub(D1, D2, ..., Dn).
So, it's basically called a "union type", and doesn't exist elsewhere in the language.
UPDATE
The effective type is the union of the nearest common super types (class and/or interfaces), i.e. you can call any method that is common for all the alternatives.
The code below illustrates the following points:
- Since both exceptions extend
SuperException
, you can call the SuperException
method.
- Since both exceptions implement interface
Foo
, you can call the Foo
method.
- The effective type is not
SuperException
, because then you couldn't call Foo
methods, and it is not Foo
, because then you couldn't call SuperException
methods.
- The effective type is truly a union of the nearest common super types, which really means all common super types.
try {
// some code throwing the exceptions
} catch (SubException1 | SubException2 e) {
e.methodInSuper(); // you can call the SuperException method
e.foo(); // you can call the Foo method
}
interface Foo {
void foo();
}
class SuperException extends Exception {
public void methodInSuper() {
// code here
}
}
class SubException1 extends SuperException implements Foo {
@Override
public void foo() {
// code here
}
}
class SubException2 extends SuperException implements Foo {
@Override
public void foo() {
// code here
}
}
UPDATE 2
To answer the exact question "what's the static type of the exception?", we need to look at the bytecode.
The bytecode of the catch clause of the above code is:
34: astore_1
35: aload_1
36: invokevirtual #33 // Method SuperException.methodInSuper:()V
39: aload_1
40: checkcast #38 // class Foo
43: invokeinterface #40, 1 // InterfaceMethod Foo.foo:()V
48: return
Exception table:
from to target type
0 34 34 Class SubException1
0 34 34 Class SubException2
As you can see, the single catch
clause registers 2 exceptions to be caught, directing both to the same code block. The call to SuperException.methodInSuper()
is done straight up. The call to Foo.foo()
is done after casting to Foo
. The compiled code can be consider equivalent to the follow, except it only catches the 2 subexceptions:
} catch (SuperException e) { // only catch SubException1 and SubException2
e.methodInSuper();
((Foo) e).foo();
}
Conclusion: The static type is the nearest common super class. Any additional common interfaces that is not defined by that super class are silently be handled by the compiler using casts.