Java lang IllegalAccess通过HashBasedTable累加器收集Guava不可变表

执行下面的代码时出错,

引起:java.lang.IllegalAccessError:试图从类访问com.google.common.collect.AbstractTable类

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

出现错误 – HashBasedTable :: putAll使用Oracle的1.8 jre

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

  • JDK-8152643 :“Javac编译方法引用,允许结果出现IllegalAccessError”
  • JDK-8059632 :“方法引用编译使用不正确的限定类型”

请注意,第一个报告的状态为“Fixed in 8u102”,因此下载JDK8u102可以解决问题。 当然,当使用除javac之外的编译器(例如ECJ)时,您必须确保该编译器也是最新的。

在任何一种情况下,您都必须重新编译源代码,因为它是编译器问题。 但是,编译后的代码甚至可以用于较旧的JRE。


为了解释这个问题,通常,调用应该使用接收器的编译时类型(或static方法的显式类型)编码到字节代码中,而不管实际方法实现的声明类型。 因此,如果您有一个publicA从非publicBinheritancepublic方法foo ,则A.foo的调用应编码为A.foo而不是B.foo 。 对于普通的调用,编译器以这种方式工作,但是对于方法引用, javac (以及afaik也是旧版本的ECJ)无法正确执行。 因此,当遇到试图直接访问B.foo但没有访问B ,会抛出IllegalAccessError

它在使用lambda表达式时起作用,因为这样,调用被编译成普通的调用指令,编译器在合成方法中正确地工作,并且在构造函数接口的实例时使用对该合成方法的引用在运行时。 由于合成方法在同一个类中,因此总是可以访问。

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