Why is this method reference failing at runtime but not the corresponding lambda call?

白昼怎懂夜的黑 提交于 2021-01-26 20:39:22

问题


I have these two interfaces. One is public (A), the other one is package private (AA). A extends AA.

package pkg.a;

@FunctionalInterface
public interface A extends AA {

}

.

package pkg.a;

interface AA {

    default void defaultM() {
        System.out.println(m());
    }

    String m();
}

I have this code (in a different package):

package pkg;

import java.util.Arrays;
import java.util.List;

import pkg.a.A;

public class Test {

    public static void main(String[] args) {
        List<A> list = Arrays.asList(() -> "imp1", () -> "imp2");

        list.stream().forEach(a -> a.defaultM());
        list.stream().forEach(A::defaultM);
    }
}

When running the above code the list.stream().forEach(A::defaultM); throws the below exception. Why? Why can't the method reference access the methods defined in the package-private interface while the lambda expression can? I'm running this in Eclipse (Version: 2018-12 (4.10.0)) with Java version 1.8.0_191.

imp1
imp2
Exception in thread "main" java.lang.BootstrapMethodError: call site initialization exception
    at java.lang.invoke.CallSite.makeSite(CallSite.java:341)
    at java.lang.invoke.MethodHandleNatives.linkCallSiteImpl(MethodHandleNatives.java:307)
    at java.lang.invoke.MethodHandleNatives.linkCallSite(MethodHandleNatives.java:297)
    at pkg.Test.main(Test.java:14)
Caused by: java.lang.IllegalArgumentException: java.lang.IllegalAccessException: class is not public: pkg.a.AA.defaultM()void/invokeInterface, from pkg.Test
    at java.lang.invoke.MethodHandles$Lookup.revealDirect(MethodHandles.java:1360)
    at java.lang.invoke.AbstractValidatingLambdaMetafactory.<init>(AbstractValidatingLambdaMetafactory.java:131)
    at java.lang.invoke.InnerClassLambdaMetafactory.<init>(InnerClassLambdaMetafactory.java:155)
    at java.lang.invoke.LambdaMetafactory.metafactory(LambdaMetafactory.java:299)
    at java.lang.invoke.CallSite.makeSite(CallSite.java:302)
    ... 3 more
Caused by: java.lang.IllegalAccessException: class is not public: pkg.a.AA.defaultM()void/invokeInterface, from pkg.Test
    at java.lang.invoke.MemberName.makeAccessException(MemberName.java:850)
    at java.lang.invoke.MethodHandles$Lookup.checkAccess(MethodHandles.java:1536)
    at java.lang.invoke.MethodHandles$Lookup.revealDirect(MethodHandles.java:1357)
    ... 7 more

回答1:


This is a bug:

Method reference uses wrong qualifying type.

A reference to a method declared in a package-access class (via a public subtype) compiles to a lambda bridge; the qualifying type in the bridge method is the declaring class, not the referenced class. This leads to an IllegalAccessError.

Fixed in Java 9.




回答2:


This appears to be a bug in certain Java versions.

I can replicate it if I compile and run it with JDK 8, specifically:

tj$ javac -version
javac 1.8.0_74
tj$ java -version
java version "1.8.0_74"
Java(TM) SE Runtime Environment (build 1.8.0_74-b02)
Java HotSpot(TM) 64-Bit Server VM (build 25.74-b02, mixed mode)

...but not with JDK 11 or 12, specifically:

tj$ javac -version
javac 11.0.1
tj$ java -version
openjdk version "11.0.1" 2018-10-16
OpenJDK Runtime Environment 18.9 (build 11.0.1+13)
OpenJDK 64-Bit Server VM 18.9 (build 11.0.1+13, mixed mode)

and

tj$ javac -version
javac 12.0.2
tj$ java -version
java version "12.0.2" 2019-07-16
Java(TM) SE Runtime Environment (build 12.0.2+10)
Java HotSpot(TM) 64-Bit Server VM (build 12.0.2+10, mixed mode, sharing)

I can also replicate it if I compile with JDK 8 but run it with JDK 12's runtime, suggesting a compilation problem.



来源:https://stackoverflow.com/questions/57755303/why-is-this-method-reference-failing-at-runtime-but-not-the-corresponding-lambda

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!