ADDITIONAL SYSTEM INFORMATION : Tested on 64-bit Windows 10 build 17713 with jdk-11-ea+24, jdk-10.0.2, and jdk1.8u181. A DESCRIPTION OF THE PROBLEM : If there are two possible targets for a lambda (say, an overloaded method), one which is Runnable, and one which is a Supplier<?>, and the lambda does not return anything, but does return abnormally i.e. contains an unconditional throw statement, the compiler infers that the lambda is a Supplier even though a lambda which returns void can never be a Supplier. The code won't actually produce a ClassCastException since calling get() on the Supplier will always throw, and any attempts I have made to alter the code to avoid throwing have caused it to switch back to compiling it to Runnable. However, I worry that I have not been clever enough, and there may be a way to get it to cause a ClassCastException at runtime after all. Originally found by StackOverflow user "Gili" in this question: https://stackoverflow.com/questions/51577332/why-does-a-lambda-change-overloads-when-it-throws-a-runtime-exception#51577332 STEPS TO FOLLOW TO REPRODUCE THE PROBLEM : Compile the attached test case, then examine the bytecode with javap -c Bug EXPECTED VERSUS ACTUAL BEHAVIOR : EXPECTED - Both the call with the lambda that prints and the call with the lambda that throws should be Runnable. ACTUAL - The first call is with a Runnable, but the second call is with a Supplier<Integer>, even though the lambda cannot possibly be a Supplier<Integer>. The bytecode for the main method produced by javac from jdk-11 is as follows: public static void main(java.lang.String[]); Code: 0: invokedynamic #2, 0 // InvokeDynamic #0:run:()Ljava/lang/Runnable; 5: invokestatic #3 // Method method:(Ljava/lang/Runnable;)V 8: invokedynamic #4, 0 // InvokeDynamic #1:get:()Ljava/util/function/Supplier; 13: invokestatic #5 // Method method:(Ljava/util/function/Supplier;)V 16: return The first call is with Runnable, as expected. The second call, with Supplier, is erroneous. ---------- BEGIN SOURCE ---------- import java.util.function.Supplier; public class Bug { public static void method(Runnable runnable) { } public static void method(Supplier<Integer> supplier) { } public static void main(String[] args) { method(() -> System.out.println()); method(() -> { throw new RuntimeException(); }); } } ---------- END SOURCE ---------- FREQUENCY : always
|