たぶん踏んだやつ
[JDK-8143647] Javac compiles method reference that allows results in an IllegalAccessError
※後述のとおり、OpenJDK ではなく Oracle JDK で起きた事象なので、同じバグかどうかは正直微妙なところ。。。
追記
Oracle JDK の Bug Database にも、同じ Issue がありました。
https://bugs.java.com/bugdatabase/view_bug.do?bug_id=8143647
起きたこと
Solaris 上の Oracle JDK 8u92 でコンパイルしたクラスが、なぜか実行時に IllegalAccessError
で落ちてしまう。
同じソースを Windows 7 (x64) 上の Eclipse Mars 2 (4.5.2) でコンパイルしたクラスだとちゃんと動く。。。
javap -c
でバイトコードを覗いてみると、メソッド参照 を使っている箇所が、ソースコード上の具象クラスではなく、その親クラスである不可視(package private
)な抽象クラスに置き換わっていた。
上記の issue の再現手順は以下の通り。
1) Create default-scoped abstract class with concrete method
2) Create a concrete public class extending the above class in the same package
3) Use a method reference to this inherited method in a class outside of the package
対して、問題の起きたクラスとメソッド参照先のクラスを簡略化したソースは、
package inner;
abstract class AbstractHoge {
public String getSomething() {
...
}
}
package inner;
public class Hoge extends AbstractHoge {
}
package outer;
import inner.Hoge;
...
public class Fuga {
public void doSomething(List<Hoge> list) {
Map<String, List<Hoge>> map =
list.stream().collect(Collectors.groupingBy(Hoge::getSomething));
...
}
}
といった感じで、見事に発動条件を満たしてしまっている。。。
解決策
上記の issue だと、8u102
だか 8u111
あたりでバックポート済みのようなので、それより新しいバージョンの Oracle JDK にアップデートする。
⇒実際に Oracle JDK 8u161 でコンパイルしたクラスでは問題が解消していた。
※何らかの制約があって JDK をアップデートができない場合は、抽象クラスのスコープを public
に変更することで暫定的に回避可能。