Architecture & DesignExploring the Intricacies of Module Access in Java 9

Exploring the Intricacies of Module Access in Java 9

Developer.com content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

The packages in a module are accessible if explicit export statements are stated in the module definition file. This is exactly what we want when we like to use the functionality of a module.  But, there are circumstances when we want a runtime access to all packages in the module even if an explicit export statement is not declared. This is exactly where the open module and opens packages keywords come into play. Thus, when a package of a module is exported, other modules can access only public types and protected members of those public types in the exported package statically at compile-time. What if we want to have a module access reflectively at runtime? This article explores this specific access mechanism of modules.

Reflection Overview

Reflection has been one of the important features of Java that enables an object to access members that are designated as private, public, or protected within a package. With modules, Java 9 introduced a topmost level of encapsulation. Therefore, the typical access mechanism through Reflection had to be revamped to work with modules as well.

Reflection, in general, provides the ability for a software component to analyze itself and describe its capabilities dynamically at runtime. For example, it enables us to determine what method, constructor, and fields a class supports. All we had to do is invoke the setAccessible(true) method on the member of the class such fields, methods, and so forth.

A value of true indicates that the reflected object should suppress Java language access checking when it is used. A value of false indicates that the reflected object should enforce Java language access checks.

Java provides this feature by the package called java.lang.reflect under the java.base module and the elements in Class. A simple reflection example to freshen up the memory is as follows.

A Quick Example

package org.mano.reflectdemo;
public class ATypicalReflectionExample {
   public static void main(String[] args) {
      String clsName = "java.math.BigInteger";
      try {
         Class<?> cls = Class.forName(clsName);
         System.out.println(clsName + " {");
         System.out.println("// Fields");
         display (cls.getFields());
         System.out.println("// Constructors");
         display (cls.getConstructors());
         System.out.println("// Methods");
         display (cls.getMethods());
         System.out.println("}");
      } catch (ClassNotFoundException e) {
         e.printStackTrace();
      }
   }
   public static void display(Object[] obj) {
      for (int i = 0; i < obj.length; i++) {
         System.out.println("  " + obj[i]);
      }
   }
}

Output

java.math.BigInteger {
   // Fields
   public static final java.math.BigInteger
      java.math.BigInteger.ZERO
   ...
   public static final java.math.BigInteger
      java.math.BigInteger.TEN
   // Constructors
   public java.math.BigInteger(byte[],int,int)
   ...
   public java.math.BigInteger(int,int,java.util.Random)
   // Methods
   public java.math.BigInteger
      java.math.BigInteger.and(java.math.BigInteger)
   ...
   public final native void java.lang.Object.notifyAll()
}

Rules of Access

Only public types in a package exported in a module are available to other modules at compile time or reflectively at runtime. There are many third-party libraries and frameworks that heavily depend upon deep reflection. Therefore, Java 9 designers had to provide a way for deep reflective access to modular code as well as a mechanism that does not violate the existing strong encapsulation rules. The access mechanism of exported types thus within a module operates according to the following norms:

  • To access types in a package at compile time, we use an export statement, and to have deep reflective access to the types at runtime, we use the open statement.
  • A module that explicitly states package export in the module definition file allows access to only public types at runtime and compile time to any interested reader of other module. A module that does not explicitly states the export statement remains inaccessible to other modules. This leverages strong encapsulation.
  • An open module is allowed to have deep reflection on all the types in the package in a particular module at runtime.
  • A module that is not open for deep reflection is called a normal module. However, some specific packages can be allowed to have deep reflection within a normal module. These packages are called open packages.

Declaring an Open Module

Declaring an open module is simple; it opens up the stated package for deep reflection. The syntax is:

open module org.mano.services{
   // Other module statements
}

This facilitates deep reflection to other modules; that means types in all its packages are accessible at runtime. Other statements, such as exports, requires, uses, and provides statements can be used in the usual manner. This also means that only those packages that are explicitly exported are accessible at compile time. Therefore, in a nutshell, an open modifier is only used in relation to reflective runtime accessibility.

Declaring Opens Packages

If we want to open a package for deep reflection, we must use an opens statement. A opens statement cannot be used inside an open module declaration. It works in two ways:

  • All types inside a package can be opened for deep reflection. The opened package will have complete reflective access at runtime.
  • A specific package can be opened to have access to another module by using a to clause. This means the opened package will have reflective access to the specific module stated with the to clause.

Both the modifiers may used, including other modifiers appropriately. Here is a sample module definition snippet to illustrate the idea open package statements.

module org.mano.services {
   opens org.mano.model;
   opens org.mano.drivers to com.appl.util;
   exports org.mano.model;
}

Conclusion

The changes brought by Java 9 have many ripple effects in everyday programming. There are numerous small and major changes that can be seen in Java code. To begin with, in modular programming the concept of accessibility is very important to understand because it establishes the relationship among modules. This article tried to put some light on reflective dynamic accessibility in modular programming as brought forth by Java 9.

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories