1. Introduction

In this tutorial, we will explore nests, the new access control context introduced in Java 11.

2. Before Java 11

2.1. Nested Types

Java allows classes and interfaces to be nested within each other. These nested types have unrestricted access to each other, including to private fields, methods, and constructors.

Consider the following nested class example:

public class Outer {

    public void outerPublic() {
    }

    private void outerPrivate() {
    }

    class Inner {

        public void innerPublic() {
            outerPrivate();
        }
    }
}

Here, although the method outerPrivate() is private, it is accessible from the method innerPublic().

We can describe a top-level type, plus all types nested within it, as forming a nest. Two members of a nest are described as nestmates.

Thus, in the above example, Outer and Inner together form a nest and are nestmates of each other.

2.2. Bridge Method

JVM access rules do not permit private access between nestmates. Ideally, we should get a compilation error for the above example. However, the Java source code compiler permits the access by introducing a level of indirection.

For example, an invocation of a private member is compiled into an invocation of a compiler-generated, package-private, bridging method in the target class, which in turn invokes the intended private method.

This happens behind the scenes. This bridging method slightly increases the size of a deployed application and can confuse users and tools.

2.3. Using Reflection

A further consequence of this is that core reflection also denies access. This is surprising given that reflective invocations should behave the same as source level invocations.

For example, if we try to call the outerPrivate() reflectively from the Inner class:

public void innerPublicReflection(Outer ob) throws Exception {
    Method method = ob.getClass().getDeclaredMethod("outerPrivate");
    method.invoke(ob);
}

We would get an exception:

java.lang.IllegalAccessException: 
Class com.baeldung.Outer$Inner can not access a member of class com.baeldung.Outer with modifiers "private"

Java 11 tries to address these concerns.

3. Nest Based Access Control

Java 11 brings the notion of nestmates and the associated access rules within the JVM. This simplifies the job of Java source code compilers.

To achieve this, the class file format now contains two new attributes:

  1. One nest member (typically the top-level class) is designated as the nest host. It contains an attribute (NestMembers) to identify the other statically known nest members.
  2. Each of the other nest members has an attribute (NestHost) to identify its nest host.

Thus, for types C and D to be nestmates they must have the same nest host. A type C claims to be a member of the nest hosted by D, if it lists D in its NestHost attribute. The membership is validated if D also lists C in its NestMembers attribute. Also, type D is implicitly a member of the nest that it hosts.

Now there is no need for the compiler to generate the bridge methods.

Finally, the nest based access control removes the surprising behavior from the core reflection. Therefore, method innerPublicReflection() shown in the previous section will execute without any exceptions.

4. Nestmate Reflection API

Java 11 provides means to query the new class file attributes using core reflection. The class java.lang.Class contains the following three new methods.

4.1. getNestHost()

This returns the nest host of the nest to which this Class object belongs:

@Test
public void whenGetNestHostFromOuter_thenGetNestHost() {
    assertEquals(NEST_HOST_NAME, Outer.class.getNestHost().getName());
}

@Test
public void whenGetNestHostFromInner_thenGetNestHost() {
    assertEquals(NEST_HOST_NAME, Outer.Inner.class.getNestHost().getName());
}

Both Outer and Inner classes belong to the nest host com.baeldung.Outer.

4.2. isNestmateOf()

This determines if the given Class is a nestmate of this Class object:

@Test
public void whenCheckNestmatesForNestedClasses_thenGetTrue() {
    assertTrue(Outer.Inner.class.isNestmateOf(Outer.class));
}

4.3. getNestMembers()

This returns an array containing Class objects representing all the members of the nest to which this Class object belongs:

@Test
public void whenGetNestMembersForNestedClasses_thenGetAllNestedClasses() {
    Set<String> nestMembers = Arrays.stream(Outer.Inner.class.getNestMembers())
      .map(Class::getName)
      .collect(Collectors.toSet());

    is(nestMembers.size()).equals(2);

    assertTrue(nestMembers.contains("com.baeldung.Outer"));
    assertTrue(nestMembers.contains("com.baeldung.Outer$Inner"));
}

5. Compilation Details

5.1. Bridge Method Before Java 11

Let’s dig into the details of the compiler generated bridging method. We can see this by disassembling the resulting class file:

$ javap -c Outer
Compiled from "Outer.java"
public class com.baeldung.Outer {
  public com.baeldung.Outer();
    Code:
       0: aload_0
       1: invokespecial #2                  // Method java/lang/Object."<init>":()V
       4: return

  public void outerPublic();
    Code:
       0: return

  static void access$000(com.baeldung.Outer);
    Code:
       0: aload_0
       1: invokespecial #1                  // Method outerPrivate:()V
       4: return
}

Here, apart from the default constructor and the public method outerPublic(), *notice the method access$000(). The compiler generates this as a bridging method.*

The innerPublic() goes through this method to call the outerPrivate():

$ javap -c Outer\$Inner
Compiled from "Outer.java"
class com.baeldung.Outer$Inner {
  final com.baeldung.Outer this$0;

  com.baeldung.Outer$Inner(com.baeldung.Outer);
    Code:
       0: aload_0
       1: aload_1
       2: putfield      #1                  // Field this$0:Lcom/baeldung/Outer;
       5: aload_0
       6: invokespecial #2                  // Method java/lang/Object."<init>":()V
       9: return

  public void innerPublic();
    Code:
       0: aload_0
       1: getfield      #1                  // Field this$0:Lcom/baeldung/Outer;
       4: invokestatic  #3                  // Method com/baeldung/Outer.access$000:(Lcom/baeldung/Outer;)V
       7: return
}

Notice the comment at line #19. Here, innerPublic() calls the bridge method access$000().

5.2. Nestmates with Java 11

The Java 11 compiler would generate the following disassembled Outer class file:

$ javap -c Outer
Compiled from "Outer.java"
public class com.baeldung.Outer {
  public com.baeldung.Outer();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public void outerPublic();
    Code:
       0: return
}

Notice that there isn’t a compiler generated bridging method. Also, the Inner class can now make a direct call to the outerPrivate() method:

$ javap -c Outer\$Inner.class 
Compiled from "Outer.java"
class com.baeldung.Outer$Inner {
  final com.baeldung.Outer this$0;

  com.baeldung.Outer$Inner(com.baeldung.Outer);
    Code:
       0: aload_0
       1: aload_1
       2: putfield      #1                  // Field this$0:Lcom/baeldung/Outer;
       5: aload_0
       6: invokespecial #2                  // Method java/lang/Object."<init>":()V
       9: return

  public void innerPublic();
    Code:
       0: aload_0
       1: getfield      #1                  // Field this$0:Lcom/baeldung/Outer;
       4: invokevirtual #3                  // Method com/baeldung/Outer.outerPrivate:()V
       7: return
}

6. Conclusion

In this article, we explored the nest based access control introduced in Java 11.

As usual, code snippets can be found over on GitHub.


« 上一篇: JAP 插入语句
» 下一篇: Java基础语法介绍