In Java 11, JVM supports the arrangement of classes and interfaces into a new access control context, called a nest.
Nests allow nested classes that are part of the same enclosing class but compiled to different class files, to access each other's private members without the need for compilers to insert synthetic generated accessibility-broadening bridge methods. This is a Java class bytecode level change.
Let's understand this feature with an example:
java-11-nested-based-access-control/src/com/logicbig/example/Test.javapackage com.logicbig.example;
public class Test {
private static int x = 5;
public static class NestedTest {
public static void doSomething() {
System.out.println(x);
}
}
}
Let's compile above class with a pre Java 11 version (say Java 10) and analyze the compiled bytecode via javap.
D:\java-11-nested-based-access-control>D:\java\jdk10\bin\javac -d out src\com\logicbig\example\Test.java
D:\java-11-nested-based-access-control>D:\java\jdk10\bin\javap out\com\logicbig\example\Test.class
Compiled from "Test.java"
public class com.logicbig.example.Test {
public com.logicbig.example.Test();
static int access$000();
static {};
}
D:\java-11-nested-based-access-control>D:\java\jdk10\bin\javap -v out\com\logicbig\example\Test.class
Classfile /D:/LogicBig/example-projects/java-11/programming-features/java-11-nested-based-access-control/out/com/logicbig/example/Test.class
Last modified Oct 2, 2018; size 430 bytes
MD5 checksum 5bd0b11979aa440ee653495cb2c5f99e
Compiled from "Test.java"
public class com.logicbig.example.Test
minor version: 0
major version: 54
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #3 // com/logicbig/example/Test
super_class: #4 // java/lang/Object
interfaces: 0, fields: 1, methods: 3, attributes: 2
Constant pool:
#1 = Fieldref #3.#19 // com/logicbig/example/Test.x:I
#2 = Methodref #4.#20 // java/lang/Object."<init>":()V
.....
{
public com.logicbig.example.Test();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #2 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 3: 0
static int access$000();
descriptor: ()I
flags: (0x1008) ACC_STATIC, ACC_SYNTHETIC
Code:
stack=1, locals=0, args_size=0
0: getstatic #1 // Field x:I
3: ireturn
LineNumberTable:
line 3: 0
static {};
descriptor: ()V
flags: (0x0008) ACC_STATIC
Code:
stack=1, locals=0, args_size=0
0: iconst_5
1: putstatic #1 // Field x:I
4: return
LineNumberTable:
line 4: 0
}
SourceFile: "Test.java"
InnerClasses:
public static #6= #5 of #3; // NestedTest=class com/logicbig/example/Test$NestedTest of class com/logicbig/example/Test
D:\java-11-nested-based-access-control>D:\java\jdk10\bin\javap -v out\com\logicbig\example\Test$NestedTest.class
Classfile /D:/java-11-nested-based-access-control/out/com/logicbig/example/Test$NestedTest.class
Last modified Oct 2, 2018; size 498 bytes
MD5 checksum 8dfda0261daaa5dc20ae891c0291a1ef
Compiled from "Test.java"
public class com.logicbig.example.Test$NestedTest
minor version: 0
major version: 54
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #5 // com/logicbig/example/Test$NestedTest
super_class: #6 // java/lang/Object
interfaces: 0, fields: 0, methods: 2, attributes: 2
Constant pool:
#1 = Methodref #6.#14 // java/lang/Object."<init>":()V
#2 = Fieldref #15.#16 // java/lang/System.out:Ljava/io/PrintStream;
#3 = Methodref #17.#18 // com/logicbig/example/Test.access$000:()I
.....
{
public com.logicbig.example.Test$NestedTest();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 6: 0
public static void doSomething();
descriptor: ()V
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=0, args_size=0
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: invokestatic #3 // Method com/logicbig/example/Test.access$000:()I
6: invokevirtual #4 // Method java/io/PrintStream.println:(I)V
9: return
LineNumberTable:
line 8: 0
line 9: 9
}
SourceFile: "Test.java"
InnerClasses:
public static #22= #5 of #17; // NestedTest=class com/logicbig/example/Test$NestedTest of class com/logicbig/example/Test
As seen above, to access enclosing class field 'Test.x' from NestedTest.doSomething() method, a bridge package-private method access$000() is syntactically generated. This is because of the reason that the outer and nested classes are compiled to different files and they need package-private visibility to access each other's private members (instance/static). From a user perspective, however, these two classes are considered to be in "the same class", and therefore users expect them to be accessed via a single common access control mechanism.
Java 11 introduces JVM level support for private access within outer/nested classes via "NestMembers" and "NestHost" attributes. ''NestMembers" corresponds to nested classes and "NestHost" corresponds to the enclosing outer class. They are generally called "Nests' or 'Nestmates'. Now outer and nested classes are linked by these attributes rather than synthetically generated package-private bridge methods.
Let's compile the above class in Java 11 and analyze the bytecode via javap:
D:\java-11-nested-based-access-control>D:\java\jdk-11\bin\javac -d out src\com\logicbig\example\Test.java
D:\java-11-nested-based-access-control>D:\java\jdk-11\bin\javap -v out\com\logicbig\example\Test.class
Classfile /D:/java-11-nested-based-access-control/out/com/logicbig/example/Test.class
Last modified Oct 1, 2018; size 387 bytes
MD5 checksum 3918d716c17fb8f23441d28f258fbaf1
Compiled from "Test.java"
public class com.logicbig.example.Test
minor version: 0
major version: 55
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #3 // com/logicbig/example/Test
super_class: #4 // java/lang/Object
interfaces: 0, fields: 1, methods: 2, attributes: 3
Constant pool:
#1 = Methodref #4.#18 // java/lang/Object."<init>":()V
#2 = Fieldref #3.#19 // com/logicbig/example/Test.x:I
.....
{
public com.logicbig.example.Test();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 3: 0
static {};
descriptor: ()V
flags: (0x0008) ACC_STATIC
Code:
stack=1, locals=0, args_size=0
0: iconst_5
1: putstatic #2 // Field x:I
4: return
LineNumberTable:
line 4: 0
}
SourceFile: "Test.java"
NestMembers:
com/logicbig/example/Test$NestedTest
InnerClasses:
public static #6= #5 of #3; // NestedTest=class com/logicbig/example/Test$NestedTest of class com/logicbig/example/Test
D:\java-11-nested-based-access-control>D:\java\jdk-11\bin\javap -v out\com\logicbig\example\Test$NestedTest.class
Classfile /D:/java-11-nested-based-access-control/out/com/logicbig/example/Test$NestedTest.class
Last modified Oct 1, 2018; size 500 bytes
MD5 checksum 82901f98555ff96c2d348d8df50199d6
Compiled from "Test.java"
public class com.logicbig.example.Test$NestedTest
minor version: 0
major version: 55
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #5 // com/logicbig/example/Test$NestedTest
super_class: #6 // java/lang/Object
interfaces: 0, fields: 0, methods: 2, attributes: 3
Constant pool:
#1 = Methodref #6.#16 // java/lang/Object."<init>":()V
#2 = Fieldref #17.#18 // java/lang/System.out:Ljava/io/PrintStream;
#3 = Fieldref #15.#19 // com/logicbig/example/Test.x:I
......
{
public com.logicbig.example.Test$NestedTest();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 6: 0
public static void doSomething();
descriptor: ()V
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=0, args_size=0
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: getstatic #3 // Field com/logicbig/example/Test.x:I
6: invokevirtual #4 // Method java/io/PrintStream.println:(I)V
9: return
LineNumberTable:
line 8: 0
line 9: 9
}
SourceFile: "Test.java"
NestHost: class com/logicbig/example/Test
InnerClasses:
public static #23= #5 of #15; // NestedTest=class com/logicbig/example/Test$NestedTest of class com/logicbig/example/Test
Multiple nested classes
Let's see another example with multiple nested classes:
java-11-nested-based-access-control/src/com/logicbig/example/Test2.javapackage com.logicbig.example;
public class Test2 {
public static class NestedTestA {
}
public static class NestedTestB {
}
}
D:\java-11-nested-based-access-control>D:\java\jdk-11\bin\javac -d out src\com\logicbig\example\Test2.java
D:\java-11-nested-based-access-control>D:\java\jdk-11\bin\javap -v out\com\logicbig\example\Test2.class
Classfile /D:/LogicBig/example-projects/java-11/programming-features/java-11-nested-based-access-control/out/com/logicbig/example/Test2.class
Last modified Oct 2, 2018; size 386 bytes
MD5 checksum 3e1b413a18d2cf38b72b87613e99a409
Compiled from "Test2.java"
public class com.logicbig.example.Test2
minor version: 0
major version: 55
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #2 // com/logicbig/example/Test2
super_class: #3 // java/lang/Object
interfaces: 0, fields: 0, methods: 1, attributes: 3
Constant pool:
#1 = Methodref #3.#16 // java/lang/Object."<init>":()V
#2 = Class #17 // com/logicbig/example/Test2
#3 = Class #18 // java/lang/Object
#4 = Class #19 // com/logicbig/example/Test2$NestedTestB
#5 = Utf8 NestedTestB
#6 = Utf8 InnerClasses
#7 = Class #20 // com/logicbig/example/Test2$NestedTestA
#8 = Utf8 NestedTestA
......
{
public com.logicbig.example.Test2();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 3: 0
}
SourceFile: "Test2.java"
NestMembers:
com/logicbig/example/Test2$NestedTestB
com/logicbig/example/Test2$NestedTestA
InnerClasses:
public static #5= #4 of #2; // NestedTestB=class com/logicbig/example/Test2$NestedTestB of class com/logicbig/example/Test2
public static #8= #7 of #2; // NestedTestA=class com/logicbig/example/Test2$NestedTestA of class com/logicbig/example/Test2
D:\java-11-nested-based-access-control>D:\java\jdk-11\bin\javap -v out\com\logicbig\example\Test2$NestedTestA.class
Classfile /D:/java-11-nested-based-access-control/out/com/logicbig/example/Test2$NestedTestA.class
Last modified Oct 2, 2018; size 313 bytes
MD5 checksum e6b269264daac4441fdad93eed0bd798
Compiled from "Test2.java"
public class com.logicbig.example.Test2$NestedTestA
minor version: 0
major version: 55
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #2 // com/logicbig/example/Test2$NestedTestA
super_class: #3 // java/lang/Object
interfaces: 0, fields: 0, methods: 1, attributes: 3
Constant pool:
#1 = Methodref #3.#12 // java/lang/Object."<init>":()V
#2 = Class #13 // com/logicbig/example/Test2$NestedTestA
#3 = Class #16 // java/lang/Object
......
{
public com.logicbig.example.Test2$NestedTestA();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 4: 0
}
SourceFile: "Test2.java"
NestHost: class com/logicbig/example/Test2
InnerClasses:
public static #14= #2 of #11; // NestedTestA=class com/logicbig/example/Test2$NestedTestA of class com/logicbig/example/Test2
D:\java-11-nested-based-access-control>D:\java\jdk-11\bin\javap -v out\com\logicbig\example\Test2$NestedTestB.class
Classfile /D:/java-11-nested-based-access-control/out/com/logicbig/example/Test2$NestedTestB.class
Last modified Oct 2, 2018; size 313 bytes
MD5 checksum 901b4a7663be45fa0237925711789606
Compiled from "Test2.java"
public class com.logicbig.example.Test2$NestedTestB
minor version: 0
major version: 55
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #2 // com/logicbig/example/Test2$NestedTestB
super_class: #3 // java/lang/Object
interfaces: 0, fields: 0, methods: 1, attributes: 3
Constant pool:
#1 = Methodref #3.#12 // java/lang/Object."<init>":()V
#2 = Class #13 // com/logicbig/example/Test2$NestedTestB
....
{
public com.logicbig.example.Test2$NestedTestB();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 6: 0
}
SourceFile: "Test2.java"
NestHost: class com/logicbig/example/Test2
InnerClasses:
public static #14= #2 of #11; // NestedTestB=class com/logicbig/example/Test2$NestedTestB of class com/logicbig/example/Test2
Reflective access
In previous versions of Java the the lack of JVM support for private access within a nest, also denies reflective access. Let's consider following example:
public class Test3 {
private static int x = 5;
public static class NestedTest {
public static void doSomething() throws Exception {
Field x = Test3.class.getDeclaredField("x");
//x.setAccessible(true);
x.setInt(null, 10);
}
}
public static void main(String[] args) throws Exception {
NestedTest.doSomething();
System.out.println(Test3.x);
}
}
If we run above code in Java 10, it will end up in an exception unless we uncomment x.setAccessible(true)
Exception in thread "main" java.lang.IllegalAccessException: class com.logicbig.example.Test3$NestedTest cannot access a member of class com.logicbig.example.Test3 with modifiers "private static"
at java.base/jdk.internal.reflect.Reflection.newIllegalAccessException(Reflection.java:360)
at java.base/java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:589)
at java.base/java.lang.reflect.Field.checkAccess(Field.java:1075)
at java.base/java.lang.reflect.Field.setInt(Field.java:958)
at com.logicbig.example.Test3$NestedTest.doSomething(Test3.java:12)
at com.logicbig.example.Test3.main(Test3.java:17)
In Java 11 we don't have to use setAccessible(true) anymore. Here's the output:
10
New Reflection API
java.lang.Class introduces three methods related to this change: getNestHost(), getNestMembers(), and isNestmateOf(). Check out examples here.
Example ProjectDependencies and Technologies Used: |
|