JEP 181: Nest-Based Access Controlというものがあります。これはNestmates(ネストメイト)と呼ばれるものです。
http://openjdk.java.net/jeps/181
Nestmatesは、クラスやインタフェースをネストして作成したとき、ネストする側される側の両方を指す言葉です。アウタークラスとインナークラスといったものです。
実はJava 10まで、インナークラスからアウタークラスのprivateメソッドを呼び出すコードを書くと、変わったバイトコード出力となっていました。こういったコードです。
public class Outer { private void m_outerpriv() { System.out.println("called m_outerpriv"); } class Inner { public void test() { new Outer().m_outerpriv(); } } public static void main(String[] args) { new Outer().new Inner().test(); } }
このとき、Outerクラスのouterpriv()メソッドの呼び出しが、access$000というstaticメソッドを経由するバイトコードとなっていました。
詳細はこのブログに一度書いていますので、前提知識として読んでいただければと思います。
参考セッション動画
JVMLS 2018でこのJEPの担当者であるKaren KinnearさんによるNestmatesのセッションがありました。私は現地にて聴きました。録画も公開されています。
Java 11とそれ以前のバイトコードを比べる
以前のバイトコード
$ javap -p -c Outer\$Inner
Compiled from "Outer.java"
class Outer$Inner {
final Outer this$0;
Outer$Inner(Outer);
Code:
0: aload_0
1: aload_1
2: putfield #1 // Field this$0:LOuter;
5: aload_0
6: invokespecial #2 // Method java/lang/Object."<init>":()V
9: return
public void test();
Code:
0: new #3 // class Outer
3: dup
4: invokespecial #4 // Method Outer."<init>":()V
7: invokestatic #5 // Method Outer.access$000:(LOuter;)V
10: return
}
InnerクラスはOuterのstaticメソッドaccess$000を呼び出します。
$ javap -p -c Outer
Compiled from "Outer.java"
public class Outer {
public Outer();
Code:
0: aload_0
1: invokespecial #2 // Method java/lang/Object."<init>":()V
4: return
private void m_outerpriv();
Code:
0: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #4 // String called m_outerpriv
5: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
...
static void access$000(Outer);
Code:
0: aload_0
1: invokespecial #1 // Method m_outerpriv:()V
4: return
}
Outerクラスにはaccess$000が合成メソッドとしてコンパイラによって作られています。access$000メソッドから本来呼び出したかったm_outerprivメソッドを呼び出しています。
Java 11
$ javap -p -c Outer\$Inner
Compiled from "Outer.java"
class Outer$Inner {
final Outer this$0;
Outer$Inner(Outer);
Code:
0: aload_0
1: aload_1
2: putfield #1 // Field this$0:LOuter;
5: aload_0
6: invokespecial #2 // Method java/lang/Object."<init>":()V
9: return
public void test();
Code:
0: new #3 // class Outer
3: dup
4: invokespecial #4 // Method Outer."<init>":()V
7: invokevirtual #5 // Method Outer.m_outerpriv:()V
10: return
}
Innerクラスはaccess$000メソッドではなくOuterクラスのm_outerprivメソッドを直接呼び出しています。
$ javap -p -c Outer
Compiled from "Outer.java"
public class Outer {
public Outer();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
private void m_outerpriv();
Code:
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #3 // String called m_outerpriv
5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
public static void main(java.lang.String[]);
Code:
0: new #5 // class Outer$Inner
3: dup
4: new #6 // class Outer
7: dup
8: invokespecial #7 // Method "<init>":()V
11: invokespecial #8 // Method Outer$Inner."<init>":(LOuter;)V
14: invokevirtual #9 // Method Outer$Inner.test:()V
17: return
}
Outerクラスにaccess$000メソッドがありません!
仕組み
このJEP 181でクラスファイルに新しい属性NestHostとNestMembersというものが追加されました。
$ javap -v Outer ... NestMembers: Outer$Inner $ javap -v Outer\$Inner ... NestHost: class Outer
ネストする側はNestMembers属性を持ち、ネストされる側はNestHost属性を持って、関係を表現しています。この属性を使って、インナークラスからアウタークラスのprivateメソッドの実行が許可されるという感じでしょう。
たったこれだけの小さなことですが、さまざまな変更が必要です。コンパイラの変更はもちろん、JVM仕様も以下のように変更される予定です。
Specification for JEP 181: Nest-based Access Control
アクセス制御の部分はもちろん、クラスファイルの属性、リフレクションAPIなどです。たとえばClassクラス。
getNestHostメソッドgetNestMembersメソッドisNestmateOfメソッド
こういったメソッドを追加しています。
まとめ
使う側には影響を及ぼしません(もちろん意図して及ぼさないようにされています)が、中にはこうした変更もJava/JVMにはあります。Nestmatesで今まで少し奇妙だった部分が解消されました!