たぶん踏んだやつ

[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 

対して、問題の起きたクラスとメソッド参照先のクラスを簡略化したソースは、

simplification
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 に変更することで暫定的に回避可能。

Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account log in.