7.6 The Multi-catch Clause
Example 7.9 uses a try block that has multiple uni-catch clauses. This example is based on Example 7.8. The sum of the values and the number of values needed to calculate the average are now read as program arguments from the command line at (2) and (3), respectively. The example shows a try statement at (1) that uses three uni-catch clauses: at (5), (6), and (7). In a uni-catch clause, a single exception type is specified for the catch parameter.
In Example 7.9, the method printAverage() is only called at (4) if there are at least two consecutive integers specified on the command line. An unchecked ArrayIndexOutOfBoundsException is thrown if there are not enough program arguments, and an unchecked NumberFormatException is thrown if an argument cannot be converted to an int value. The astute reader will notice that the code for handling these two exceptions is the same in the body of the respective catch clauses. In order to avoid such code duplication, one might be tempted to replace the two catch clauses with a single catch clause that catches a more general exception, for example:
catch (RuntimeException rte) { // NOT RECOMMENDED!
System.out.println(rte);
System.out.println(“Usage: java Average9 <sum of values> <no. of values>”);
}
This is certainly not recommended, as specific exceptions are to be preferred over general exceptions, not the least because a more general exception type might unintentionally catch more exceptions than intended.
Example 7.9 Using Multiple catch Clauses
// File: IntegerDivisionByZero.java
public class IntegerDivisionByZero extends Exception {
IntegerDivisionByZero() { super(“Integer Division by Zero”); }
}
// File: Average9.java
public class Average9 {
public static void main(String[] args) {
try { // (1)
int sum = Integer.parseInt(args[0]); // (2)
int numOfValues = Integer.parseInt(args[1]); // (3)
printAverage(sum, numOfValues); // (4)
} catch (ArrayIndexOutOfBoundsException aioob) { // (5) uni-catch
System.out.println(aioob);
System.out.println(“Usage: java Average9 <sum of values> <no. of values>”);
} catch (NumberFormatException nfe) { // (6) uni-catch
System.out.println(nfe);
System.out.println(“Usage: java Average9 <sum of values> <no. of values>”);
} catch (IntegerDivisionByZero idbz) { // (7) uni-catch
idbz.printStackTrace();
System.out.println(“Exception handled in main().”);
} finally { // (8)
System.out.println(“Finally done in main().”);
}
System.out.println(“Exit main().”); // (9)
}
public static void printAverage(int totalSum, int totalCount)
throws IntegerDivisionByZero {
int average = computeAverage(totalSum, totalCount);
System.out.println(“Average = ” +
totalSum + ” / ” + totalCount + ” = ” + average);
System.out.println(“Exit printAverage().”);
}
public static int computeAverage(int sum, int count)
throws IntegerDivisionByZero {
System.out.println(“Computing average.”);
if (count == 0)
throw new IntegerDivisionByZero();
return sum/count;
}
}
Running the program:
>
java Average9 100 twenty
java.lang.NumberFormatException: For input string: “twenty”
Usage: java Average9 <sum of values> <no. of values>
Finally done in main().
Exit main().
Running the program:
>
java Average9 100
java.lang.ArrayIndexOutOfBoundsException: 1
Usage: java Average9 <sum of values> <no. of values>
Finally done in main().
Exit main().
The multi-catch clause provides the solution, allowing specific exceptions to be declared and avoiding duplicating the same code for the body of the catch clauses. The syntax of the multi-catch clause is as follows:
catch (
exception_type
1
|
exception_type
2
|…|
exception_type
k
parameter
) {
statements
}
The multi-catch clause still has a single parameter, but now a list of exception types, delimited by the vertical bar (|), can be specified as the types for this parameter. This list defines a union of alternatives that are the exception types which the multi-catch clause can handle. The statements in the body of the multi-catch clause will be executed when an object of any of the specified exception types is caught by the multi-catch clause.
The multiple catch clauses at (5) and (6) in Example 7.9 have been replaced with a multi-catch clause at (5) in Example 7.10:
catch (ArrayIndexOutOfBoundsException | // (5) multi-catch
NumberFormatException ep) {
System.out.println(ep);
System.out.println(“Usage: java Average10 <sum of values> <no. of values>”);
}
The multi-catch clause in Example 7.10 is semantically equivalent to the two uni-catch clauses in Example 7.9, and we can expect the same program behavior in both examples.
Example 7.10 Using the Multi-catch Clause
// File: Average10.java
public class Average10 {
public static void main(String[] args) {
try { // (1)
int sum = Integer.parseInt(args[0]); // (2)
int numOfValues = Integer.parseInt(args[1]); // (3)
printAverage(sum, numOfValues); // (4)
} catch (ArrayIndexOutOfBoundsException | // (5) multi-catch
NumberFormatException ep) {
System.out.println(ep);
System.out.println(“Usage: java Average10 <sum of values> <no. of values>”);
} catch (IntegerDivisionByZero idbz) { // (6) uni-catch
idbz.printStackTrace();
System.out.println(“Exception handled in main().”);
} finally { // (7)
System.out.println(“Finally done in main().”);
}
System.out.println(“Exit main().”); // (8)
}
public static void printAverage(int totalSum, int totalCount)
throws IntegerDivisionByZero {
// See Example 7.9.
}
public static int computeAverage(int sum, int count)
throws IntegerDivisionByZero {
// See Example 7.9.
}
}
A few remarks are in order regarding the alternatives of a multi-catch clause. There should be no subtype–supertype relationship between any of the specified exception types in the alternatives of a multi-catch clause. The following multi-catch clause will not compile, as ArrayIndexOutOfBoundsException is a subtype of IndexOutOfBoundsException:
catch (IndexOutOfBoundsException | // Compile-time error!
ArrayIndexOutOfBoundsException e) {
// …
}
The parameter of a multi-catch clause is also considered to be implicitly final, and therefore cannot be assigned to in the body of the multi-catch clause. In a uni-catch clause, the parameter is considered to be effectively final if it does not occur on the left-hand side of an assignment in the body of the uni-catch clause.
try {
// Assume appropriate code to throw the right exceptions.
} catch (NumberFormatException |
IndexOutOfBoundsException e) { // Parameter is final.
e = new ArrayIndexOutOfBoundsException();// Compile-time error!
// Cannot assign to final parameter e.
} catch (IntegerDivisionByZero idbz) { // Parameter is effectively final.
idbz.printStackTrace();
} catch (IOException ioe) { // Parameter is not effectively final.
ioe = new FileNotFoundException(“No file.”);
}
Disallowing any subtype–supertype relationship between alternatives and the parameter being final in a multi-catch clause or effectively final in a uni-catch clause allows the compiler to perform precise exception handling analysis.
The compiler also generates effective bytecode for a single exception handler corresponding to all the alternatives in a multi-catch clause, in contrast to generating bytecode for multiple exception handlers for uni-catch clauses that correspond to the multi-catch clause.