JDK-8047338 : javac is not correctly filtering non-members methods to obtain the function descriptor
  • Type: Bug
  • Component: tools
  • Sub-Component: javac
  • Affected Version: 8u5
  • Priority: P4
  • Status: Closed
  • Resolution: Fixed
  • OS: os_x
  • CPU: x86
  • Submitted: 2014-06-13
  • Updated: 2019-05-30
  • Resolved: 2016-08-24
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 9
9 b134Fixed
Related Reports
Duplicate :  
Relates :  
Description
FULL PRODUCT VERSION :
java version "1.8.0_05"
Java(TM) SE Runtime Environment (build 1.8.0_05-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.5-b02, mixed mode)

ADDITIONAL OS VERSION INFORMATION :
Mac OS X 10.9.3

A DESCRIPTION OF THE PROBLEM :
Consider this snippet of java 8 code:

public class Generics {
  public static <V, E extends Exception> V f(CheckedCallable1<V, E> callable) throws E {
    return callable.call();
  }
  public static <V, E extends Exception> V g(CheckedCallable2<V, E> callable) throws E {
    return callable.call();
  }
  public static void main(String[] args) throws Exception {
    f(() -> 1);
    g(() -> 1);
  }
}

interface Callable<V> {
  V call() throws Exception;
}

interface CheckedCallable1<V, E extends Exception> {
  V call() throws E;
}

interface CheckedCallable2<V, E extends Exception> extends Callable<V> {
  @Override V call() throws E;
}

The lambda at the call to f compiles fine, whereas the lambda at the call to g does not compile, but rather gives this compile error:

Error:(10, 7) java: call() in <anonymous Generics$> cannot implement call() in CheckedCallable2
  overridden method does not throw java.lang.Exception

I believe this is a compiler bug, in particular since the identical code with Exception replaced with IOException compiles fine.

Additional details are at http://stackoverflow.com/questions/24199148/lambdas-and-functional-interfaces-with-generic-throw-clauses

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Compile the following java code:

public class Generics {
  public static <V, E extends Exception> V f(CheckedCallable1<V, E> callable) throws E {
    return callable.call();
  }
  public static <V, E extends Exception> V g(CheckedCallable2<V, E> callable) throws E {
    return callable.call();
  }
  public static void main(String[] args) throws Exception {
    f(() -> 1);
    g(() -> 1);
  }
}

interface Callable<V> {
  V call() throws Exception;
}

interface CheckedCallable1<V, E extends Exception> {
  V call() throws E;
}

interface CheckedCallable2<V, E extends Exception> extends Callable<V> {
  @Override V call() throws E;
}

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
I expect this code to compile.
ACTUAL -
The call to g results in the following compile error:

Error:(10, 7) java: call() in <anonymous Generics$> cannot implement call() in CheckedCallable2
  overridden method does not throw java.lang.Exception

ERROR MESSAGES/STACK TRACES THAT OCCUR :
Error:(10, 7) java: call() in <anonymous Generics$> cannot implement call() in CheckedCallable2
  overridden method does not throw java.lang.Exception

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
public class Generics {
  public static <V, E extends Exception> V f(CheckedCallable1<V, E> callable) throws E {
    return callable.call();
  }
  public static <V, E extends Exception> V g(CheckedCallable2<V, E> callable) throws E {
    return callable.call();
  }
  public static void main(String[] args) throws Exception {
    f(() -> 1);
    g(() -> 1);
  }
}

interface Callable<V> {
  V call() throws Exception;
}

interface CheckedCallable1<V, E extends Exception> {
  V call() throws E;
}

interface CheckedCallable2<V, E extends Exception> extends Callable<V> {
  @Override V call() throws E;
}
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
1) Don't extend a functional interface without a generic throws clause in a functional interface with a generic throws clause.  

or

2) Use a subclass of Exception on the functional interface without generic throws clause.


Comments
this bug was showing two problems in javac: - the one I commented in the comment above, which should be addressed as a separate issue. - a problem determining the right members when determining the function descriptor The second problem is more important and it's the one to be addressed first. Reusing this simpler code: interface Callable<V> { V callFail(String s) throws Exception; } interface CheckedCallableFail<V, Efail extends Exception> extends Callable<V> { @Override V callFail(String s) throws Efail; } For this specific test case it implies that when determining the function descriptor for interface CheckedCallableFail, javac considered as members of the interface the method CheckedCallableFail::callFail and method Callable::callFail, when only CheckedCallableFail::callFail is a member of the interface as it's a subsignature of the method defined in Callable
24-08-2016

reduced test case: interface Callable<V> { V callFail(String s) throws Exception; } interface CheckedCallableFail<V, Efail extends Exception> extends Callable<V> { @Override V callFail(String s) throws Efail; } Related spec: At 9.9 Function types it says: .... ��� throws clause: The function type's throws clause is derived from the throws clauses of the methods in M. If the function type is generic, these clauses are first adapted to the type parameters of the function type (��8.4.4). If the function type is not generic but at least one method in M is generic, these clauses are first erased. in this case M = {CheckedCallableFail::callFail} and there should be no erasure as the function type is not generic but there are no generic methods in M
18-08-2016