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
方法的显式类型)编码到字节代码中,而不管实际方法实现的声明类型。 因此,如果您有一个public
类A
从非public
类B
inheritancepublic
方法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)) );