BT

Java Nestmates Makes Progress

| by Ben Evans Follow 31 Followers on Mar 15, 2018. Estimated reading time: 4 minutes |

Oracle has announced JEP 181 - "Nest-based Access Control" - aka "Nestmates". This is a technical enhancement to the platform that pays off a 20 year old architectural debt introduced in Java 1.1

The new feature is connected to the implementation of Java's nested classes (which are often loosely referred to as "inner classes", despite this being only one possible type of nested class).

In general, nested types are used for two separate purposes, both related to encapsulation. First, a type may be only required for a very specific reason, and in a very small section of code. This means that it should be tightly localized, as it is really part of the implementation detail. In older versions of Java, the only way to do this was with a nested type, such as an anonymous implementation of an interface. In practice, with the advent of Java 8, this use case has substantially been taken over by lambda expressions and the use of anonymous types as closely-localized types has dramatically declined, although it still persists for some cases.

Second, a type may be nested because it needs especially intimate access to the internals of another type. By being a nested (i.e. member) type, it has access in the same way that member variables and methods do. This means that nested types have privileged access and can be thought of as "slightly bending the rules of encapsulation".

Another way of thinking about this use case of nested types is that they are types that are somehow tied together with another type. This means that they don’t really have a completely independent existence as an entity, and only live in coexistence with another type.

The purpose of the Nestmates JEP is to generalize and formalize this symbiotic relationship between types, and to clean up the current implementation, which has a certain amount of technical debt and is rather hacky to modern eyes.

As of Java 10, nested classes are compiled into separate top-level classfiles, but with a special naming convention, so that a nested class Nested within a class Outer is compiled into a file named Outer$Nested.class The problem with this implementing strategy is that according to the rules of the Java language, a nested class has access to all the members of the enclosing class, including private members.

To solve this problem, javac adds additional synthetic accessor methods to Outer to allow access. For example, this simple inner class:

public class Outer {
    private int i = 0;
    
    public class Inner {
        public int i() {
            return i;
        }
    }
}

becomes two class files, Outer.class and Outer$Inner.class with bytecode:

public class Outer {
  private int i;

  public Outer();
    Code:
       0: aload_0
       1: invokespecial #2                  // Method java/lang/Object."<init>":()V
       4: aload_0
       5: iconst_0
       6: putfield      #1                  // Field i:I
       9: return

  static int access$000(Outer);
    Code:
       0: aload_0
       1: getfield      #1                  // Field i:I
       4: ireturn
}

and

public class Outer$Inner {
  final Outer this$0;

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

  public int i();
    Code:
       0: aload_0
       1: getfield      #1                  // Field this$0:LOuter;
       4: invokestatic  #3                  // Method Outer.access$000:(LOuter;)I
       7: ireturn
}

The private access required by the inner class has been transformed by the compiler into a package-private accessor method access$000() on Outer. The existence of this synthetic accessor means that developers who know about the mechanism can access it, either directly or via reflection - even if they are not a nestmate of the original class.

With the steps being taken to tidy up access control as part of Java's roadmap, this aspect of nested types stands out as being in need of a cleanup, or as the JEP describes the motivation:

A formal notion of a group of class files forming a nest, where nestmates share a common access control mechanism, allows the desired result to be directly achieved in a simpler, more secure, more transparent manner.

The JEP description also notes that future enhancements could include:

  • In generic specialization, each specialized type could be created as a nestmate of the generic type.
  • A safe and supported replacement for the Unsafe.defineAnonymousClass() API could create the new class as a nestmate of an existing class.
  • The concept of "sealed classes" could be effected by only allowing subclasses that are nestmates.
  • Truly private nested types could be effected (presently private nested types are defined with package-access).

Sealed classes and truly private types are likely to be popular with developers working in Scala and other languages where they are supported, as they provide the building blocks necessary for implementing ideas such as algebraic data types that are useful in advanced features such as pattern matching.

Nestmates is being developed as part of Project Valhalla, and the work to produce an initial working prototype is well underway, and is being actively worked on by OpenJDK developers.

As is usual for long-range features, Oracle has not made any commitment as to when (if ever) Nestmates will ship. However, given its usefulness to other landmark features (such as sealed classes and pattern matching) that are currently in development, interested Java programmers should keep an eye out as the project matures.

Rate this Article

Adoption Stage
Style

Hello stranger!

You need to Register an InfoQ account or or login to post comments. But there's so much more behind being registered.

Get the most out of the InfoQ experience.

Tell us what you think

Allowed html: a,b,br,blockquote,i,li,pre,u,ul,p

Email me replies to any of my messages in this thread

Whom are you protecting from? by Cameron Purdy

Doesn't the requirement for "sealed" classes indicate a design flaw in the first place? Sorry, but if you're able to break security by extending a class, then the runtime wasn't secure to start with.

The C#/.NET approach ("sealed") drives me nuts, so I hope others are not copying it. "When in doubt, seal it." So you end up with relatively useless abstractions provided by the runtime library, because all of the constituent parts are sealed (I'm looking at you, DictionaryEntry). The result is that you are permitted by the gracious kindness of the API designers to be a passive API consumer (a serf), but you can't augment any of the APIs. How disgustingly patronizing, unless of course you believe that one company can provide every necessary API and do so without missing any necessary details. (Hint: A closed walled garden may work well for a consumer device like a phone, but sucks for a programming language. See also: Religion and priesthood.)

Java may require final classes because of a lack of support for real immutability (@see String), and perhaps because of a poorly designed class loading/linking system (no transitive closure over the type system, and thus implicit mutability of the type system.) But to frame this as a security issue is to acknowledge a pretty enormous (and arguably unfixable) design flaw, which would be far better addressed with actual immutability.

Re: Whom are you protecting from? by Ben Evans

Hi Cameron.

I think there might be some confusion here.

Firstly, sealed classes are only one of the features that nestmates enables. This is really a tidying up and alignment of the VM implementation of an existing Java language feature that is currently provided a nasty hack (synthetic accessors).

But, to address sealed classes directly - Java already has final, so I'm not sure I see the concern you're worried about. My understanding is that if sealed is added to the language, it will be used more in the Scala way - to mean that a class is not extensible outside the current compilation unit.

This means I can declare, eg an Expr type as a sealed abstract class, and within it I can declare nested concrete final subclasses. These are then the only subtypes of Expr that are allowed to exist, and thus can be used to implement sum types, just as in Scala.

Given that pattern matching seems to be quite likely to materialize for Java, I would very much want algebraic data types to turn up as well - which means we need a mechanism for sum types, and this looks to me like a reasonable way of achieving that.

Re: Whom are you protecting from? by Cameron Purdy

My points are:

1) providing bandages to protect access to private state is not a substitute for JVM-enforced immutability; and

2) use of final/sealed on a type almost always indicates a flaw somewhere else (see above).

Re: Whom are you protecting from? by Ben Evans

1) Immutability is mostly orthogonal to access to private state. Some types are just not perfectly encapsulated away from each other and require a higher level of access to each others internals than is usual. You could argue that is due to a failure of OOD on the part of the implementor, but I've seen plenty of cases where no other reasonable design made sense.

2) Again, "sealed" here does not means the C# use of the keyword, instead what it probably means is close to the Scala usage, which is about providing certainty to type pattern matching among other things.

You are allowed to feel that final classes are overused in the Java language. Equally, I am entitled to believe that they are very substantially *underused* and that a lot of developer-written classes make too much unnecesary use of inheritance rather than being constrained by evil library designers.

Perhaps if you gave some examples of popular open-source libraries that are overly encumbered by having to work around overuse of final?

Re: Whom are you protecting from? by Cameron Purdy

1) Immutability is mostly orthogonal to access to private state. Some types are just not perfectly encapsulated away from each other and require a higher level of access to each others internals than is usual.


Agreed; I was not conflating the two. It is Java that conflates the two, by employing "final" as a poor man's immutability mechanism.

2) Again, "sealed" here does not means the C# use of the keyword, instead what it probably means is close to the Scala usage, which is about providing certainty to type pattern matching among other things.


I assume due to a lack of transient closure over an immutable type system, thus allowing "surprise" types to show up and be introduced to running code unexpectedly.

You are allowed to feel that final classes are overused in the Java language. Equally, I am entitled to believe that they are very substantially *underused* and that a lot of developer-written classes make too much unnecesary use of inheritance rather than being constrained by evil library designers.


There is no shortage of crap APIs and libraries; on this you will get no argument from me. Inheritance is certainly overused and abused; on this you get no argument from me.

The example that I had given was from the .NET standard libraries, in which they used a struct (which is automatically sealed) instead of an interface as part of a common, necessary, and supposedly user-implementable API, probably for the same inane reason that they default to non-virtual methods, i.e. to save a hypothetical clock cycle somewhere.

The problem with other people's short-sightedness is that sooner or later, it becomes an obstacle for the rest of us to do our jobs.

Perhaps if you gave some examples of popular open-source libraries that are overly encumbered by having to work around overuse of final?


That's a tough question to answer. Most of the libraries that I've used that made all classes and methods final by default also did a fair job at design, so the fact that they're all final never got in my way.

My preferred model is one in which my type system is as immutable as I want it to be, which means that someone else extending and screwing with my types cannot be made visible to me against my will, and then to allow them to do with my types whatever they please (since my type system, once linked, is immutable). But that's just my way of saying "give them whatever rope they need to hang themselves, but don't let them mess up my little bubble".

Re: Whom are you protecting from? by Ben Evans

Looks like we're not actually as far apart as I thought we were - and I'd missed that your original example was actually from .NET

I'd like to hear you flesh out your ideas for how such a hypothetical type system might work (along all of the axes you describe) maybe that's a conference bar conversation sometime.

Re: Whom are you protecting from? by Cameron Purdy

Ben - sounds great ... maybe, I'll just show you? ;-)

Allowed html: a,b,br,blockquote,i,li,pre,u,ul,p

Email me replies to any of my messages in this thread

Allowed html: a,b,br,blockquote,i,li,pre,u,ul,p

Email me replies to any of my messages in this thread

7 Discuss

Login to InfoQ to interact with what matters most to you.


Recover your password...

Follow

Follow your favorite topics and editors

Quick overview of most important highlights in the industry and on the site.

Like

More signal, less noise

Build your own feed by choosing topics you want to read about and editors you want to hear from.

Notifications

Stay up-to-date

Set up your notifications and don't miss out on content that matters to you

BT