JDK-7190387 : multi-catch incorrectly exemplified as sequence of uni-catch
  • Type: Enhancement
  • Component: specification
  • Sub-Component: language
  • Affected Version: 7
  • Priority: P4
  • Status: Closed
  • Resolution: Fixed
  • OS: linux_ubuntu
  • CPU: x86
  • Submitted: 2012-08-09
  • Updated: 2014-02-26
  • Resolved: 2013-08-19
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 8
8Fixed
Related Reports
Relates :  
Description
FULL PRODUCT VERSION :
$ java -version
java version "1.7.0_05"
Java(TM) SE Runtime Environment (build 1.7.0_05-b05)
Java HotSpot(TM) 64-Bit Server VM (build 23.1-b03, mixed mode)


ADDITIONAL OS VERSION INFORMATION :
$ uname -a
Linux d-ubuntu12x64-02 3.2.0-23-generic #36-Ubuntu SMP Tue Apr 10 20:39:51 UTC 2012 x86_64 x86_64 x86_64 GNU/Linux


A DESCRIPTION OF THE PROBLEM :
Section 14.20, "The try statement" of JLS-7 (JSR-000901) erroneously describes multi-catch clauses as semantically equivalent to a sequence of uni-catch clauses on page 403 (of the PDF form available at http://docs.oracle.com/javase/specs).  The erroneous text is:

<begin quote>
A multi-catch clause can be thought of as a sequence of uni-catch clauses.  That is, a  catchclause whose exception parameter type is denoted as a union  D1|D2|...|Dn is equivalent to a sequence of n catch clauses whose exception parameters have class types D1, D2, ..., Dn, respectively. For example, the following code:

try {
    ... throws ReflectiveOperationException ...
}
catch (ClassNotFoundException | IllegalAccessException ex) {
    ... body ...
}

is semantically equivalent to the following code:

try {
    ... throws ReflectiveOperationException ...
} catch (final ClassNotFoundException ex1) {
    ... body ...
} catch (final IllegalAccessException ex2) {
    ... body ...
}

whereby the multi-catch clause with two alternatives has been translated into two separate catch clauses, one for each alternative. A Java compiler is neither required nor recommended to compile a multi-catch clause by duplicating code in this manner, since it is possible to represent the multi-catchclause in a classfile without duplication.
<end quote>

As written, each body in the uni-catch code variant would be evaluated using the final class type as specified in each catch clause.  This contrasts with the multi-catch code variant in which the body of the multi-catch clause would be evaluated using the least upper bound super type of the multi-catch clause parameter.  This difference affects method overload resolution as can be seen in the sample code included with this bug report.

The text can be corrected by replacing the uni-catch clause code variant as follows and noting that ReflectiveOperationException is the least upper bound super type of ClassNotFoundException and IllegalAccessException.

try {
    ... throws ReflectiveOperationException ...
} catch (final ClassNotFoundException ex1) {
    final ReflectiveOperationException ex = ex1;
    ... body ...
} catch (final IllegalAccessException ex2) {
    final ReflectiveOperationException ex = ex2;
    ... body ...
}

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
The source code included with this bug report demonstrates that a naive translation of a multi-catch clause into a sequence of uni-catch clauses is not necessarily semantically equivalent.  The test case contains two try statements.  The first uses a multi-catch clause and the second uses only uni-catch clauses expanded as described in the JLS 7 text.  Running the program illustrates that different results are produced.


REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
public class t {
    public static void main(String[] args) {
        try {
            if (args.length == 0 || args[0].equals("1")) {
                throw new ExceptionType1();
            } else {
                throw new ExceptionType2();
            }
        } catch(ExceptionType1 | ExceptionType2 ex) {
            DoSomething(ex);
        }

        try {
            if (args.length == 0 || args[0].equals("1")) {
                throw new ExceptionType1();
            } else {
                throw new ExceptionType2();
            }
        } catch(final ExceptionType1 ex) {
            DoSomething(ex);
        } catch(final ExceptionType2 ex) {
            DoSomething(ex);
        }
    }

    static class ExceptionTypeBase extends Exception { }
    static class ExceptionType1 extends ExceptionTypeBase { }
    static class ExceptionType2 extends ExceptionTypeBase { }

    static void DoSomething(ExceptionTypeBase ex) {
        System.out.println("DoSomething(ExceptionTypeBase)");
    }

    static void DoSomething(ExceptionType1 ex) {
        System.out.println("DoSomething(ExceptionType1)");
    }

    static void DoSomething(ExceptionType2 ex) {
        System.out.println("DoSomething(ExceptionType2)");
    }
}

---------- END SOURCE ----------

Comments
EVALUATION Good catch. JLS7 states "The declared type of an exception parameter that denotes its type as a union with alternatives D1 | D2 | ... | Dn is lub(D1, D2, ..., Dn)". Therefore, in the example showing a multi-catch translated to multiple uni-catch, the exception parameters should not be stated as having class types D1, D2, ..., Dn. The lub must be taken for the exception parameter of each and every uni-catch.
14-08-2012