Java 11 – Nest-Based Access Control
This JEP 181: Nest-Based Access Control, supports private access within nest members directly, no more via an auto-generated bridge method access$000
. Furthermore, new nest APIs for validation and allowed private reflection access within nest members.
P.S No need to change the code, it is a Java compiler’s optimization to remove the bridge method access.
1. Before Java 11, bridge method access$000
1.1 Review the following nested class. All nested classes are try to access a private member of Alphabet
, aka NestHost
.
public class Alphabet {
private String name = "I'm Alphabet!";
public class A {
public void printName() {
System.out.println(name); // access Alphabet's private member!
}
}
public class B {
public void printName() {
System.out.println(name); // access Alphabet's private member!
}
public class B1 {
public void printName() {
System.out.println(name); // access Alphabet's private member!
}
}
}
}
1.2 If we compile the above source, it will generate four classes, Alphabet
, Alphabet$A
, Alphabet$B
, and Alphabet$B.B1
, even a nested class is a typical class with a unique name. The JVM access rule will not allow private access within different classes.
However, Java allowed private access within nest members, so the Java compiler creates a bridge method access$000
to apply on the JVM access rule.
$ javac Alphabet.java
Alphabet$A.class
Alphabet$B.class
Alphabet$B.B1.class
Alphabet.class
1.3 The source code of the bridge method access is similar to this:
public class Alphabet {
private String name = "I'm Alphabet!";
String access$000(){
return name;
}
}
public class Alphabet$A {
final Alphabet obj;
public void printName(){
System.out.println(obj.access$000());
}
}
public class Alphabet$B {
final Alphabet obj;
public void printName(){
System.out.println(obj.access$000());
}
}
public class Alphabet$B$B1 {
final Alphabet$B this$1;
public void printName(){
System.out.println(obj.access$000());
}
}
2. Disassemble the code
We can use javap -c
to disassemble the above four classes to see the differential before and after bridge method access.
2.1 Before Java 11, it creates a new access$000
bridge method for the nested private access automatically.
$ javac Alphabet.java
$ javap -c Alphabet
Compiled from "Alphabet.java"
public class Alphabet {
public Alphabet();
Code:
0: aload_0
1: invokespecial #2 // Method java/lang/Object."<init>":()V
4: aload_0
5: ldc #3 // String I'm Alphabet!
7: putfield #1 // Field name:Ljava/lang/String;
10: return
static java.lang.String access$000(Alphabet);
Code:
0: aload_0
1: getfield #1 // Field name:Ljava/lang/String;
4: areturn
}
$ javap -c Alphabet$A
Compiled from "Alphabet.java"
public class Alphabet$A {
final Alphabet this$0;
public Alphabet$A(Alphabet);
Code:
0: aload_0
1: aload_1
2: putfield #1 // Field this$0:LAlphabet;
5: aload_0
6: invokespecial #2 // Method java/lang/Object."<init>":()V
9: return
public void printName();
Code:
0: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
3: aload_0
4: getfield #1 // Field this$0:LAlphabet;
7: invokestatic #4 // Method Alphabet.access$000:(LAlphabet;)Ljava/lang/String;
10: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
13: return
}
$ javap -c Alphabet$B
Compiled from "Alphabet.java"
public class Alphabet$B {
final Alphabet this$0;
public Alphabet$B(Alphabet);
Code:
0: aload_0
1: aload_1
2: putfield #1 // Field this$0:LAlphabet;
5: aload_0
6: invokespecial #2 // Method java/lang/Object."<init>":()V
9: return
public void printName();
Code:
0: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
3: aload_0
4: getfield #1 // Field this$0:LAlphabet;
7: invokestatic #4 // Method Alphabet.access$000:(LAlphabet;)Ljava/lang/String;
10: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
13: return
}
$ javap -c Alphabet$B$B1
Compiled from "Alphabet.java"
public class Alphabet$B$B1 {
final Alphabet$B this$1;
public Alphabet$B$B1(Alphabet$B);
Code:
0: aload_0
1: aload_1
2: putfield #1 // Field this$1:LAlphabet$B;
5: aload_0
6: invokespecial #2 // Method java/lang/Object."<init>":()V
9: return
public void printName();
Code:
0: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
3: aload_0
4: getfield #1 // Field this$1:LAlphabet$B;
7: getfield #4 // Field Alphabet$B.this$0:LAlphabet;
10: invokestatic #5 // Method Alphabet.access$000:(LAlphabet;)Ljava/lang/String;
13: invokevirtual #6 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
16: return
}
2.2 In Java 11, it supports private access within nest members directly, with NO bridge method.
Tested with Java 11.
$ javac Alphabet.java
$ javap -c Alphabet
Compiled from "Alphabet.java"
public class Alphabet {
public Alphabet();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: aload_0
5: ldc #2 // String I\'m Alphabet!
7: putfield #3 // Field name:Ljava/lang/String;
10: return
}
$ javap -c Alphabet$A
Compiled from "Alphabet.java"
public class Alphabet$A {
final Alphabet this$0;
public Alphabet$A(Alphabet);
Code:
0: aload_0
1: aload_1
2: putfield #1 // Field this$0:LAlphabet;
5: aload_0
6: invokespecial #2 // Method java/lang/Object."<init>":()V
9: return
public void printName();
Code:
0: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
3: aload_0
4: getfield #1 // Field this$0:LAlphabet;
7: getfield #4 // Field Alphabet.name:Ljava/lang/String;
10: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
13: return
}
$ javap -c Alphabet$B
Compiled from "Alphabet.java"
public class Alphabet$B {
final Alphabet this$0;
public Alphabet$B(Alphabet);
Code:
0: aload_0
1: aload_1
2: putfield #1 // Field this$0:LAlphabet;
5: aload_0
6: invokespecial #2 // Method java/lang/Object."<init>":()V
9: return
public void printName();
Code:
0: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
3: aload_0
4: getfield #1 // Field this$0:LAlphabet;
7: getfield #4 // Field Alphabet.name:Ljava/lang/String;
10: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
13: return
}
$ javap -c Alphabet$B$B1
Compiled from "Alphabet.java"
public class Alphabet$B$B1 {
final Alphabet$B this$1;
public Alphabet$B$B1(Alphabet$B);
Code:
0: aload_0
1: aload_1
2: putfield #1 // Field this$1:LAlphabet$B;
5: aload_0
6: invokespecial #2 // Method java/lang/Object."<init>":()V
9: return
public void printName();
Code:
0: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
3: aload_0
4: getfield #1 // Field this$1:LAlphabet$B;
7: getfield #4 // Field Alphabet$B.this$0:LAlphabet;
10: getfield #5 // Field Alphabet.name:Ljava/lang/String;
13: invokevirtual #6 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
16: return
}
3. New Nest Classes APIs.
3.1 Some new APIs to validate the nested members.
getNestHost
getNestMembers
isNestmateOf
For example, review the following nested classes. The Alphabet
is NestHost
, and all others are NestMembers
or NestmateOf
to each other.
public class Alphabet {
public class A {
}
public class B {
public class B1 {
}
}
}
3.2 The below source code shows the use of the new nested APIs. The only trick is the B.B1.class.getNestHost()
returns Alphabet
, not B
. The other results are obvious, read the comments.
package com.mkyong.java11.jep181;
import java.util.Arrays;
public class Alphabet {
private String name = "I'm Alphabet!";
public class A {
public void printName() {
System.out.println(name); // access Alphabet's private member!
}
}
public class B {
public void printName() {
System.out.println(name); // access Alphabet's private member!
}
public class B1 {
public void printName() {
System.out.println(name); // access Alphabet's private member!
}
}
}
public static void main(String[] args) {
A objA = new Alphabet().new A();
objA.printName();
B objB = new Alphabet().new B();
objB.printName();
B.B1 objB1 = new Alphabet().new B().new B1();
objB1.printName();
System.out.println(Alphabet.class.getNestHost()); // Alphabet
System.out.println(A.class.getNestHost()); // Alphabet
System.out.println(B.class.getNestHost()); // Alphabet
System.out.println(B.B1.class.getNestHost()); // Alphabet!, not B
System.out.println("---");
System.out.println(Arrays.toString(Alphabet.class.getNestMembers())); // Alphabet, Alphabet$A, Alphabet$B, Alphabet$B$B1
System.out.println(Arrays.toString(A.class.getNestMembers())); // Alphabet, Alphabet$A, Alphabet$B, Alphabet$B$B1
System.out.println(Arrays.toString(B.class.getNestMembers())); // Alphabet, Alphabet$A, Alphabet$B, Alphabet$B$B1
System.out.println(Arrays.toString(B.B1.class.getNestMembers())); // Alphabet, Alphabet$A, Alphabet$B, Alphabet$B$B1
System.out.println("---");
System.out.println(Alphabet.class.isNestmateOf(Alphabet.class)); // true
System.out.println(Alphabet.class.isNestmateOf(A.class)); // true
System.out.println(Alphabet.class.isNestmateOf(B.class)); // true
System.out.println(Alphabet.class.isNestmateOf(B.B1.class)); // true
System.out.println("---");
System.out.println(A.class.isNestmateOf(Alphabet.class)); // true
System.out.println(A.class.isNestmateOf(A.class)); // true
System.out.println(A.class.isNestmateOf(B.class)); // true
System.out.println(A.class.isNestmateOf(B.B1.class)); // true
System.out.println("---");
System.out.println(B.class.isNestmateOf(Alphabet.class)); // true
System.out.println(B.class.isNestmateOf(A.class)); // true
System.out.println(B.class.isNestmateOf(B.class)); // true
System.out.println(B.class.isNestmateOf(B.B1.class)); // true
System.out.println("---");
System.out.println(B.B1.class.isNestmateOf(Alphabet.class)); // true
System.out.println(B.B1.class.isNestmateOf(A.class)); // true
System.out.println(B.B1.class.isNestmateOf(B.class)); // true
System.out.println(B.B1.class.isNestmateOf(B.B1.class)); // true
}
}
4. Reflection Access
3.1 The InnerA
tries to access the InnerB
private method via reflection API.
package com.mkyong.java11.jep181;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Outer {
public static class InnerA {
// InnerA access InnerB private method, via reflection!
// Before Java 11 - IllegalAccessException
// Java 11 - OK
public void printName() throws NoSuchMethodException,
InvocationTargetException, IllegalAccessException {
InnerB obj = new InnerB();
final Method m = InnerB.class.getDeclaredMethod("printName");
m.invoke(obj);
}
}
public static class InnerB {
// private!!!
private void printName() {
System.out.println("I'm InnerB!");
}
}
public static void main(String[] args) throws NoSuchMethodException,
IllegalAccessException, InvocationTargetException {
InnerA obj = new InnerA();
obj.printName();
}
}
Before Java 11, it throws java.lang.IllegalAccessException
.
Exception in thread "main" java.lang.IllegalAccessException: Class com.mkyong.java11.jep181.Outer$InnerA can not access a member of class com.mkyong.java11.jep181.Outer$InnerB with modifiers "private"
at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:102)
at java.lang.reflect.AccessibleObject.slowCheckMemberAccess(AccessibleObject.java:296)
at java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:288)
at java.lang.reflect.Method.invoke(Method.java:491)
at com.mkyong.java11.jep181.Outer$InnerA.printName(Outer.java:14)
at com.mkyong.java11.jep181.Outer.main(Outer.java:28)
Run with Java 11, no problem. The new nest-based access control allows private access to nested members, even via reflection API.
I'm InnerB!
Download Source Code
$ git clone https://github.com/mkyong/core-java
$ cd java-11
$ cd src/main/java/com/mkyong/java11/jep181
This article is very interesting
This channel is making tutorials in french for java certification https://bit.ly/2XpG9Q8