Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

JEP 181 Incompatibility: Dealing With Nesting Classes

DZone's Guide to

JEP 181 Incompatibility: Dealing With Nesting Classes

Java 11 fixed the behavioral error when dealing with nested classes. Click here to learn more about the JEP 181 incompatibility and dealing with nesting classes.

· Java Zone ·
Free Resource

Verify, standardize, and correct the Big 4 + more– name, email, phone and global addresses – try our Data Quality APIs now at Melissa Developer Portal!

JEP 181 is a nest-based access control introduced in Java 11, and it deliberately introduced an incompatibility with previous versions. This is a good example explaing that being compatible with prior versions of Java is not a rule carved into stone, but it rather is to keep the consistency and steady development of the language. In this article, I will look at the change with an example that I came across a few years ago and how Java 11 makes life easier and more consistent in this special case.

Java backward compatibility is limited to features, but not to behavior.

Before Java 11

A few years ago, I wrote an article on ScriptBasic for Java interpreters and how they can be extended with Java methods. I wanted to make it so that they were available just as if they were written in BASIC, so I created unit tests. The unit test class contained inner classes that had a method available for the BASIC code. The inner class was static and private as it had nothing to do with any other classes except the test; however, the class and methods were still accessible to the test code because they resided in the same class. To my dismay, the methods were not accessible via the BASIC programs. When I tried to call the methods through the BASIC interpreter, which itself was using reflective access, I got IllegalAccessException.

To rectify the situation, I created the following simple code after a few hours of debugging and learning:

package javax0;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class ReflThrow {
    private class Nested {
        private void m(){
            System.out.println("m called");
        }
    }
    public static void main(String[] args)
            throws NoSuchMethodException,
            InvocationTargetException,
            IllegalAccessException {
        ReflThrow me = new ReflThrow();
        Nested n = me.new Nested();
        n.m();
        Method m = Nested.class.getDeclaredMethod("m");
        m.invoke(n);
    }
}


If you run this code with Java N where N < 11, then you get something similar to this:

m called
Exception in thread "main" java.lang.IllegalAccessException: class ReflThrow cannot access a member of class ReflThrow$Nested with modifiers "private"
    at java.base/jdk.internal.reflect.Reflection.throwIllegalAccessException(Reflection.java:423)
    at java.base/jdk.internal.reflect.Reflection.throwIllegalAccessException(Reflection.java:414)
...


It works, however, using Java 11 (and presumably, it will also work fine with later versions of Java).

Explanation

Up to version 11 of the JVM, it did not handle inner and nested classes. All classes in the JVM were top-level classes. The Java compiler created a specially named top-level class from the inner and nested classes. For example, one of the Java compilers may create the class files ReflThrow.class and ReflThrow$Nested.class, but because they are top-level classes for the JVM, the code in the class ReflThrow cannot invoke the private method m() of Nested when there are two different top-level classes.

On the Java level, however, when these classes are created from a nested structure, it is possible. To make it happen, the compiler creates an extra synthetic method inside the class Nestedthat the code in ReflThrow can call, and this method, which is already inside Nested, calls m().

The synthetic methods have the modifier SYNTHETIC so that the compiler later knows that other code should not “see” those methods. That way, invoking the method m() works nicely.
On the other hand, when we try to call the method m() using its name and reflective access, the route goes directly through the class boundaries without invoking any synthetic method, and because the method is private to the class it is in, the invocation throws the exception.

Java 11 changes this. The JEP 181 incorporated into the already released Java 11 introduces the notion nest. “Nests allow classes that are logically part of the same code entity, but which are compiled to distinct class files, to access each other’s private members without the need for compilers to insert accessibility —broadening bridge methods.” It simply means that there are classes in which there are nests, and there are classes that belong to a nest. When the code is generated from Java, then the top level class is the nesting class and the classes inside are nested. This structure of the JVM-level leaves a lot of room for different language structures and does not put an octroi of a Java structure on the execution environment. The JVM is aimed to be polyglot and is going to be even “more” polyglot with the introduction of the GraalVM in the future. The JVM using this structure simply sees that two classes are in the same nest, thus they can access each other private methods, fields, and other members. This also means that there are no bridge methods with different access restrictions, and that way, reflection goes through exactly the same access boundaries as the normal Java call.

Takeaway

Java does not change overnight and is mostly backward compatible. Backward compatibility is, however, limited to features and not to behavior. The JEP 181 did not, and it never actually intended to reproduce the not absolutely perfect IllegalAccessException throwing behavior of the reflective access to nested classes. This behavior was rather an implementation behavior/bug rather than a lauguage feature and was fixed in Java 11.

Developers! Quickly and easily gain access to the tools and information you need! Explore, test and combine our data quality APIs at Melissa Developer Portal – home to tools that save time and boost revenue. 

Topics:
java ,jep 181 ,nesting ,nesting classes ,incompatability ,java 11 ,jvm

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}