Java 11 - java.lang.Class Changes for Nest-Based Access Control (JEP 181)
Java 11 introduces the concept of nests, where two members of the same nest are nestmates. Two new attributes are defined for the class file format, NestHost and NestMembers. These changes are useful for other languages that support nested classes and are compiled to bytecodes.
This feature introduces three new methods to java.lang.Class:
- Class getNestHost()
- Class[] getNestMembers()
- boolean isNestmateOf(Class):
public Class<?> getNestHost()
Returns the nest host of the nest to which the class or interface represented by this Class object belongs.
public Class<?>[] getNestMembers()
Returns an array containing Class objects representing all the classes and interfaces that are members of the nest to which the class or interface represented by this Class object belongs.
public boolean isNestmateOf​(Class<?> c)
Determines if the given Class is a nestmate of the class or interface represented by this Class object.
Let's create an example to read nesting information:
package com.dariawan.jdk11;
import java.util.Arrays;
import java.util.stream.Collectors;
public class JEP181Nest {
public class Nested1 {
public class Nested1ClasA {
}
public class Nested1ClasB {
}
}
public class Nested2 {
}
public static void main(String[] args) {
testNests(JEP181Nest.class);
testNests(Nested1.class);
testNests(Nested2.class);
testNests(Nested1.Nested1ClasA.class);
testNests(Nested1.Nested1ClasB.class);
System.out.println("*** testIsNestmateOf: ***");
testIsNestmateOf(JEP181Nest.class, Nested1.class);
testIsNestmateOf(JEP181Nest.class, Nested2.class);
testIsNestmateOf(JEP181Nest.class, Nested1.Nested1ClasA.class);
testIsNestmateOf(JEP181Nest.class, Nested1.Nested1ClasB.class);
testIsNestmateOf(Nested1.class, Nested1.Nested1ClasA.class);
testIsNestmateOf(Nested1.class, Nested1.Nested1ClasB.class);
testIsNestmateOf(Nested1.class, Nested2.class);
testIsNestmateOf(Nested2.class, Nested1.Nested1ClasA.class);
testIsNestmateOf(Nested2.class, Nested1.Nested1ClasB.class);
}
private static void testNests(Class<?> cls) {
System.out.printf("*** Nests for class: %s ***%n", cls.getSimpleName());
System.out.println("Nest Host:");
System.out.println(cls.getNestHost().getSimpleName());
Class<?>[] nestMembers = cls.getNestMembers();
System.out.println("Nest Members:\n" +
Arrays.stream(nestMembers).map(Class::getSimpleName)
.collect(Collectors.joining("\n")));
}
private static void testIsNestmateOf(Class<?> cls1, Class<?> cls2) {
System.out.printf("%s isNestmateOf %s = %s%n",
cls1.getSimpleName(), cls2.getSimpleName(), cls1.isNestmateOf(cls2));
}
}
And when we run:
$ java -cp . com.dariawan.jdk11.JEP181Nest
The output is:
*** Nests for class: JEP181Nest *** Nest Host: JEP181Nest Nest Members: JEP181Nest Nested2 Nested1 Nested1ClasB Nested1ClasA *** Nests for class: Nested1 *** Nest Host: JEP181Nest Nest Members: JEP181Nest Nested2 Nested1 Nested1ClasB Nested1ClasA *** Nests for class: Nested2 *** Nest Host: JEP181Nest Nest Members: JEP181Nest Nested2 Nested1 Nested1ClasB Nested1ClasA *** Nests for class: Nested1ClasA *** Nest Host: JEP181Nest Nest Members: JEP181Nest Nested2 Nested1 Nested1ClasB Nested1ClasA *** Nests for class: Nested1ClasB *** Nest Host: JEP181Nest Nest Members: JEP181Nest Nested2 Nested1 Nested1ClasB Nested1ClasA *** testIsNestmateOf: *** JEP181Nest isNestmateOf Nested1 = true JEP181Nest isNestmateOf Nested2 = true JEP181Nest isNestmateOf Nested1ClasA = true JEP181Nest isNestmateOf Nested1ClasB = true Nested1 isNestmateOf Nested1ClasA = true Nested1 isNestmateOf Nested1ClasB = true Nested1 isNestmateOf Nested2 = true Nested2 isNestmateOf Nested1ClasA = true Nested2 isNestmateOf Nested1ClasB = true
You can see that all classes sharing same Nest Host (JEP181Nest) and same Nest members. So if you first thinking that nest host of Nested1ClasA is Nested1, you got it wrong. Javap command will show you the bytecode produced in JEP181Nest class
*** JAVAP RESULT TRUNCATED START *** SourceFile: "JEP181Nest.java" NestMembers: com/dariawan/jdk11/JEP181Nest$Nested2 com/dariawan/jdk11/JEP181Nest$Nested1 com/dariawan/jdk11/JEP181Nest$Nested1$Nested1ClasB com/dariawan/jdk11/JEP181Nest$Nested1$Nested1ClasA InnerClasses: public #30= #5 of #2; // Nested2=class com/dariawan/jdk11/JEP181Nest$Nested2 of class com/dariawan/jdk11/JEP181Nest public #32= #4 of #2; // Nested1=class com/dariawan/jdk11/JEP181Nest$Nested1 of class com/dariawan/jdk11/JEP181Nest public #69= #6 of #4; // Nested1ClasA=class com/dariawan/jdk11/JEP181Nest$Nested1$Nested1ClasA of class com/dariawan/jdk11/JEP181Nest$Nested1 public #71= #7 of #4; // Nested1ClasB=class com/dariawan/jdk11/JEP181Nest$Nested1$Nested1ClasB of class com/dariawan/jdk11/JEP181Nest$Nested1 public static final #155= #154 of #159; // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles *** JAVAP RESULT TRUNCATED END ***
And Nested1ClasA class:
*** JAVAP RESULT TRUNCATED START *** SourceFile: "JEP181Nest.java" NestHost: class com/dariawan/jdk11/JEP181Nest InnerClasses: public #7= #6 of #21; // Nested1=class com/dariawan/jdk11/JEP181Nest$Nested1 of class com/dariawan/jdk11/JEP181Nest public #16= #3 of #6; // Nested1ClasA=class com/dariawan/jdk11/JEP181Nest$Nested1$Nested1ClasA of class com/dariawan/jdk11/JEP181Nest$Nested1 *** JAVAP RESULT TRUNCATED END ***
The reason is they are from same source file: JEP181Nest.java.
Nest membership is "always" recorded in the class file of the top-level class or interface (the nest host - in our case: JEP181Nest). That class file must be present at run time to allow the access control checks to be performed.