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.