Your search did not match any results.
We suggest you try the following to help find what you’re looking for:
What they are and how to use them
By Paul Deitel
Paul Deitel
In this article, I introduce the Java 9 Platform Module System (JPMS), the most important new software engineering technology in Java since its inception. Modularity—the result of Project Jigsaw—helps developers at all levels be more productive as they build, maintain, and evolve software systems, especially large systems.
Modularity adds a higher level of aggregation above packages. The key new language element is the module—a uniquely named, reusable group of related packages, as well as resources (such as images and XML files) and a module descriptor specifying
The Java SE platform has been around since 1995. There are now approximately 10 million developers using it to build everything from small apps for resource-constrained devices—like those in the Internet of Things (IoT) and other embedded devices—to large-scale business-critical and mission-critical systems. There are massive amounts of legacy code out there, but until now, the Java platform has primarily been a monolithic one-size-fits-all solution. Over the years, there have been various efforts geared to modularizing Java, but none is widely used—and none could be used to modularize the Java platform.
Modularizing the Java SE platform has been challenging to implement, and the effort has taken many years. JSR 277: Java Module System was originally proposed in 2005 for Java 7. This JSR was later superseded by JSR 376: Java Platform Module System and targeted for Java 8. The Java SE platform is now modularized in Java 9, but only after Java 9 was delayed until September 2017.
Each module must explicitly state its dependencies.
According to JSR 376, the key goals of modularizing the Java SE platform are
JEP 200: THE MODULAR JDK
JEP 201: MODULAR SOURCE CODE
JEP 220: MODULAR RUN-TIME IMAGES
JEP 260: ENCAPSULATE MOST INTERNAL APIS
JEP 261: MODULE SYSTEM
JEP 275: MODULAR JAVA APPLICATION PACKAGING
JEP 282: JLINK: THE JAVA LINKER
JSR 376: JAVA PLATFORM MODULE SYSTEM
JSR 379: JAVA SE 9
Table 1. Java Modularity JEPs and JSRs
A crucial aspect of Java 9 is dividing the JDK into modules to support various configurations. (Consult “JEP 200: The Modular JDK.” All the Java modularity JEPs and JSRs are shown in Table 1.) Using the java command from the JDK’s bin folder with the --list-modules
option, as in:
java --list-modules
lists the JDK’s set of modules, which includes the standard modules that implement the Java Language SE Specification (names starting with java
), JavaFX modules (names starting with javafx
), JDK-specific modules (names starting with jdk
) and Oracle-specific modules (names starting with oracle
). Each module name is followed by a version string—@9
indicates that the module belongs to Java 9.
As we mentioned, a module must provide a module descriptor—metadata that specifies the module’s dependencies, the packages the module makes available to other modules, and more. A module descriptor is the compiled version of a module declaration that’s defined in a file named module-info.java
. Each module declaration begins with the keyword module
, followed by a unique module name and a module body enclosed in braces, as in:
A key motivation of the module system is strong encapsulation.
module modulename {
}
The module declaration’s body can be empty or may contain various module directives, including requires
, exports
, provides…with
, uses
and opens
(each of which we discuss). As you’ll see later, compiling the module declaration creates the module descriptor, which is stored in a file named module-info.class
in the module’s root folder. Here we briefly introduce each module directive. After that, we’ll present actual module declarations.
The keywords exports
, module
, open
, opens
, provides
, requires
, uses
, with
, as well as to
and transitive
, which we introduce later, are restricted keywords. They’re keywords only in module declarations and may be used as identifiers anywhere else in your code.
requires. A requires
module directive specifies that this module depends on another module—this relationship is called a module dependency. Each module must explicitly state its dependencies. When module A requires
module B, module A is said to read module B and module B is read by module A. To specify a dependency on another module, use requires
, as in:
requires modulename;
There is also a requires static
directive to indicate that a module is required at compile time, but is optional at runtime. This is known as an optional dependency and won’t be discussed in this introduction.
requires transitive—implied readability. To specify a dependency on another module and to ensure that other modules reading your module also read that dependency—known as implied readability—use requires transitive
, as in:
requires transitive modulename;
Consider the following directive from the java.desktop
module declaration:
requires transitive java.xml
;
In this case, any module that reads java.desktop
also implicitly reads java.xml
. For example, if a method from the java.desktop
module returns a type from the java.xml
module, code in modules that read java.desktop
becomes dependent on java.xml
. Without the requires transitive
directive in java.desktop
’s module declaration, such dependent modules will not compile unless they explicitly read java.xml
.
According to JSR 379 Java SE’s standard modules must grant implied readability in all cases like the one described here. Also, though a Java SE standard module may depend on non-standard modules, it must not grant implied readability to them. This ensures that code depending only on Java SE standard modules is portable across Java SE implementations.
exports and exports…to. An exports
module directive specifies one of the module’s packages whose public
types (and their nested public
and protected
types) should be accessible to code in all other modules. An exports…to
directive enables you to specify in a comma-separated list precisely which module’s or modules’ code can access the exported package—this is known as a qualified export.
uses. A uses
module directive specifies a service used by this module—making the module a service consumer. A service is an object of a class that implements the interface or extends the abstract
class specified in the uses
directive.
provides…with. A provides…with
module directive specifies that a module provides a service implementation—making the module a service provider. The provides
part of the directive specifies an interface or abstract
class listed in a module’s uses
directive and the with
part of the directive specifies the name of the service provider class that implements
the interface or extends
the abstract
class.
open, opens, and opens…to. Before Java 9, reflection could be used to learn about all types in a package and all members of a type—even its private
members—whether you wanted to allow this capability or not. Thus, nothing was truly encapsulated.
A key motivation of the module system is strong encapsulation. By default, a type in a module is not accessible to other modules unless it’s a public type and you export its package. You expose only the packages you want to expose. With Java 9, this also applies to reflection.
Allowing runtime-only access to a package. An opens module directive of the form
opens package
indicates that a specific package’s public
types (and their nested public
and protected
types) are accessible to code in other modules at runtime only. Also, all the types in the specified package (and all of the types’ members) are accessible via reflection.
Allowing runtime-only access to a package by specific modules. An opens…to
module directive of the form
opens package to comma-separated-list-of-modules
indicates that a specific package’s public
types (and their nested public
and protected
types) are accessible to code in the listed modules at runtime only. All of the types in the specified package (and all of the types’ members) are accessible via reflection to code in the specified modules.
Allowing runtime-only access to all packages in a module. If all the packages in a given module should be accessible at runtime and via reflection to all other modules, you may open
the entire module, as in:
open module modulename {
// module directives
}
By default, a module with runtime reflective access to a package can see the package’s public
types (and their nested public
and protected
types). However, the code in other modules can access all types in the exposed package and all members within those types, including private
members via setAccessible
, as in earlier Java versions.
For more information on setAccessible
and reflection, see Oracle’s documentation.
Paul Deitel, CEO and chief technical officer of Deitel & Associates, is a graduate of MIT with 35 years of experience in computing. He is a Java Champion and has been programming in Java for more than 22 years. He and his coauthor, Dr. Harvey M. Deitel, are the world’s best-selling programming-language authors. Paul has delivered Java, Android, iOS, C#, C++, C, and internet programming courses to industry, government, and academic clients internationally.
NOTE: This article is excerpted from Java Magazine September/October 2017.