【问题标题】:Why is this method reference failing at runtime but not the corresponding lambda call?为什么这个方法引用在运行时失败,而不是相应的 lambda 调用?
【发布时间】:2019-09-02 21:19:40
【问题描述】:

我有这两个接口。一种是公共的(A),另一种是包私有的(AA)。 A 扩展了 AA

package pkg.a;

@FunctionalInterface
public interface A extends AA {

}

.

package pkg.a;

interface AA {

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

    String m();
}

我有这个代码(在不同的包中):

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);
    }
}

运行上述代码时,list.stream().forEach(A::defaultM); 会引发以下异常。为什么?为什么方法引用不能访问包私有接口中定义的方法,而 lambda 表达式可以?我在 Java 版本 1.8.0_191 的 Eclipse(版本:2018-12 (4.10.0))中运行它。

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

【问题讨论】:

  • 这是一个很好问的问题。它有一个minimal reproducible example(即使在 JDK 8 中也会为我复制错误),很明显......很高兴看到!
  • @AndrewTobilko - 它编译,但它没有运行(对我来说在 JDK 8 [我有什么方便],特别是 1.8.0_74 使用“Java HotSpot(TM ) 64 位服务器 VM(内部版本 25.74-b02,混合模式)")。 “线程“主”java.lang.IllegalAccessError 中的异常:试图从类 pkg.Test 访问类 pkg.a.AA”
  • 它使用“OpenJDK 64-Bit Server VM 18.9(内部版本 11.0.1+13,混合模式)”在“openjdk 版本“11.0.1”2018-10-16”中为我运行。听起来像是一个已修复的错误。
  • 更新了所用 Java 版本的问题。将尝试使用更高版本的 java 并报告。
  • 似乎bugs.eclipse.org/bugs/show_bug.cgi?id=480930(从2015年...)相同,并且从那个链接的javac中有一些相关问题。

标签: java eclipse


【解决方案1】:

这是一个错误:

Method reference uses wrong qualifying type.

对在包访问类中声明的方法的引用(通过 public subtype) 编译为 lambda 桥;符合条件的类型 bridge 方法是声明类,而不是引用类。 这会导致 IllegalAccessError。

在 Java 9 中已修复。

【讨论】:

    【解决方案2】:

    这似乎是某些 Java 版本中的错误。

    如果我用 JDK 8 编译和运行它,我可以复制它,特别是:

    tj$ javac -版本 javac 1.8.0_74 tj$ java-版本 java版本“1.8.0_74” Java(TM) SE 运行时环境 (build 1.8.0_74-b02) Java HotSpot(TM) 64 位服务器 VM(内部版本 25.74-b02,混合模式)

    ...但不适用于 JDK 11 或 12,特别是:

    tj$ javac -版本 javac 11.0.1 tj$ java-版本 openjdk 版本“11.0.1” 2018-10-16 OpenJDK 运行时环境 18.9(内部版本 11.0.1+13) OpenJDK 64 位服务器 VM 18.9(内部版本 11.0.1+13,混合模式)

    tj$ javac -版本 javac 12.0.2 tj$ java-版本 java版本“12.0.2” 2019-07-16 Java(TM) SE 运行时环境 (build 12.0.2+10) Java HotSpot(TM) 64 位服务器 VM(内部版本 12.0.2+10,混合模式,共享)

    如果我使用 JDK 8 编译但使用 JDK 12 的运行时运行它,我也可以复制它,这表明存在编译问题。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-09-13
      • 2012-03-14
      • 1970-01-01
      • 2011-09-07
      • 2014-11-13
      相关资源
      最近更新 更多