Why does a lambda change overloads when it throws a runtime exception?

前端 未结 5 1152
心在旅途
心在旅途 2021-01-31 02:14

Bear with me, the introduction is a bit long-winded but this is an interesting puzzle.

I have this code:

public class Testcase {
    public static void m         


        
5条回答
  •  孤独总比滥情好
    2021-01-31 02:37

    I wrongly considered this a bug, but it appears to be correct according to §15.27.2. Consider:

    import java.util.function.Supplier;
    
    public class Bug {
        public static void method(Runnable runnable) { }
    
        public static void method(Supplier supplier) { }
    
        public static void main(String[] args) {
            method(() -> System.out.println());
            method(() -> { throw new RuntimeException(); });
        }
    }
    
    javac Bug.java
    javap -c Bug
    public static void main(java.lang.String[]);
      Code:
         0: invokedynamic #2,  0      // InvokeDynamic #0:run:()Ljava/lang/Runnable;
         5: invokestatic  #3          // Method add:(Ljava/lang/Runnable;)V
         8: invokedynamic #4,  0      // InvokeDynamic #1:get:()Ljava/util/function/Supplier;
        13: invokestatic  #5          // Method add:(Ljava/util/function/Supplier;)V
        16: return
    

    This happens with jdk-11-ea+24, jdk-10.0.1, and jdk1.8u181.

    zhh's answer led me to find this even simpler test case:

    import java.util.function.Supplier;
    
    public class Simpler {
        public static void main(String[] args) {
            Supplier s = () -> { throw new RuntimeException(); };
        }
    }
    

    However, duvduv pointed out §15.27.2, in particular, this rule:

    A block lambda body is value-compatible if it cannot complete normally (§14.21) and every return statement in the block has the form return Expression;.

    Thus, a block lambda is trivially value-compatible even if it contains no return statement at all. I would have thought, because the compiler needs to infer its type, that it would require at least one return Expression;. Holgar and others have pointed out that this is not necessary with ordinary methods such as:

    int foo() { for(;;); }
    

    But in that case the compiler only needs to ensure there is no return that contradicts the explicit return type; it doesn't need to infer a type. However, the rule in the JLS is written to allow the same freedom with block lambdas as with ordinary methods. Perhaps I should have seen that sooner, but I did not.

    I filed a bug with Oracle but have since sent an update to it referencing §15.27.2 and stating that I believe my original report to be in error.

提交回复
热议问题