Java lang 非法访问通过 HashBasedTable 累加器收集 Guava Immutable Table

Java lang IllegalAccess on collecting Guava Immutable Table via HashBasedTable accumulator

执行以下代码时出错,

Caused by: java.lang.IllegalAccessError: tried to access class com.google.common.collect.AbstractTable from class

 ImmutableTable.copyOf(listItemsToProcess.parallelStream()
                    .map(item ->
                            ProcessorInstanceProvider.getInstance()
                            .buildImmutableTable(item))
                    .collect(() -> HashBasedTable.create(),
                            HashBasedTable::putAll,

                            HashBasedTable<Integer, String,    
                            Boolean>::putAll)
                    );

Error in coming on - HashBasedTable::putAll Using Oracle's 1.8 jre

AbstractTable 是在 Guava 版本 15 中引入的。看看你的类路径配置;您可能在运行时使用了较早的库版本。

有趣的是,我用 Lambda 表达式替换了方法引用并且它起作用了。

    ImmutableTable.copyOf(itemList.parallelStream()
                    .map(item ->
                            ProcessorInstanceProvider.get()
                            .buildImmutableTable(item))
                    .collect(() -> HashBasedTable.create(),
                            (a, b) -> a.putAll(b),
                            (a, b) -> a.putAll(b))
                    );

这是一个编译器错误,相关报告是

  • JDK-8152643:“Javac 编译的方法引用允许导致 IllegalAccessError”
  • JDK-8059632:“方法引用编译使用了不正确的限定类型”

请注意,第一份报告的状态为“已在 8u102 中修复”,因此下载 JDK8u102 可以解决问题。当然,当使用 javac 以外的编译器时,例如ECJ,你必须确保编译器也是最新的。

无论哪种情况,您都必须重新编译源代码,因为这是编译器问题。但是,编译后的代码甚至可以与较旧的 JRE 一起使用。


为了解释这个问题,通常情况下,调用应该使用接收器的编译时类型(或 static 方法中的显式类型)编码到字节码中,而不考虑声明类型的实际方法实现。因此,如果您有一个 public class A 从非 public class [=17] 继承 public 方法 foo =],对 A.foo 的调用应编码为 A.foo 而不是 B.foo。对于普通调用,编译器以这种方式工作,但对于方法引用,javac(以及 afaik 也是旧版本的 ECJ)未能正确地做到这一点。因此,当遇到 class 试图直接访问 B.foo 而无法访问 B 时,将抛出 IllegalAccessError

它在使用 lambda 表达式时有效,因为那时,调用被编译成普通的调用指令,编译器在合成方法中正常工作,并且在构造运行时功能接口的实例。由于合成方法在同一 class 中,因此始终可以访问。