Close

Java 11 - Nested-Based Access Control (JEP 181)

[Last Updated: Oct 2, 2018]

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.java

package 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.java

package 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 Project

Dependencies and Technologies Used:

  • JDK 9.0.1
Nested-Based Access Control Select All Download
  • java-11-nested-based-access-control
    • src
      • com
        • logicbig
          • example
            • Test.java

    See Also