Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8182047

javac compile error on type-parameter-exceptions in lambda expressions

XMLWordPrintable

    • b14
    • x86_64
    • generic

      FULL PRODUCT VERSION :
      java version "1.8.0_131"
      Java(TM) SE Runtime Environment (build 1.8.0_131-b11)
      Java HotSpot(TM) 64-Bit Server VM (build 25.131-b11, mixed mode)

      ADDITIONAL OS VERSION INFORMATION :
      Microsoft Windows [Version 6.1.7601]
      and others

      A DESCRIPTION OF THE PROBLEM :
      Java compiler doesn't compile valid code: when declaring a functional interface that has an exception as a generic type parameter (<E extends Exception>) and when the function of that interface actually throws another, non-generic Exception, the order of the exceptions in the throws clause matters.

      Looking in the Java language spec (https://docs.oracle.com/javase/specs/jls/se8/html/jls-18.html - 18.2.5 Checked Exception Constraints) provides the following on resolving exception types:

      "If n > 0, the constraint reduces to a set of subtyping constraints: for all i (1 ≤ i ≤ m), if Xi is not a subtype of any proper type in the throws clause, then the constraints include, for all j (1 ≤ j ≤ n), ‹Xi <: Ej›. In addition, for all j (1 ≤ j ≤ n), the constraint reduces to the bound throws Ej."

      This doesn't specify the order of exceptions (i.e. first generic, then "proper" or vice versa) should matter.

      It seems (to me as a non-expert...) that JavaC tries to resolve the two exceptions thrown by the lambda expression and finds "MyEx2" to fit just fine to "E", thus binds E, even though MyEx2 itself is directly available to satisfy the thrown exception.

      (The source sample uses a function reference instead of a lambda - the results are the same.)

      (Actually, note that the Eclipse Java compiler (Eclipse Neon.3) does compile the source just fine)

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      Try to compile the provided source code.

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      error free compilation
      ACTUAL -
      compile error

      ERROR MESSAGES/STACK TRACES THAT OCCUR :
      unreported exception JavaCError.MyExBase; must be caught or declared to be thrown

      REPRODUCIBILITY :
      This bug can be reproduced always.

      ---------- BEGIN SOURCE ----------
      public class JavaCError {

      public static class MyExBase extends Exception {
      private static final long serialVersionUID = 1L;
      }

      public static class MyEx1 extends MyExBase {
      private static final long serialVersionUID = 1L;
      }

      public static class MyEx2 extends MyExBase {
      private static final long serialVersionUID = 1L;
      }

      public interface MyLambdaIF1 <E extends Exception> {
      void lambdaFun() throws E, MyEx2; // <<<< generic exception first
      }

      public interface MyLambdaIF2 <E extends Exception> {
      void lambdaFun() throws MyEx2, E; // <<<< specific exception first
      }

      public <E extends Exception> void fun1(MyLambdaIF1<E> myLambda) throws E, MyEx2 {
      myLambda.lambdaFun();
      }

      public <E extends Exception> void fun2(MyLambdaIF2<E> myLambda) throws E, MyEx2 {
      myLambda.lambdaFun();
      }

      public void useIt1() throws MyEx1, MyEx2 {
      fun1(this::lambda); // <<<< compile error
      }

      public void useIt1a() throws MyExBase {
      fun1(this::lambda); // <<<< using base exception type instead helps
      }

      public void useIt2() throws MyEx1, MyEx2 {
      fun2(this::lambda); // <<<< reversed order in throws clause helps
      }

      public void lambda() throws MyEx1, MyEx2 {
      if (Math.random() > 0.5)
      throw new MyEx2();
      throw new MyEx1();
      }
      }
      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      Changing the order of exceptions in the throw clause of the functional interface fixes the problem: declaring the specific exception MyEx2 first, and the generically provided exception E second, gives the expected result.

      Alternatively, you could declare (or catch) the base exception type MyExBase, but that changes the semantics.

            vromero Vicente Arturo Romero Zaldivar
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            5 Start watching this issue

              Created:
              Updated:
              Resolved: