After its review concluded, JEP 476, Module Import Declarations (Preview), was integrated into JDK 23. This preview feature proposes to enhance the Java programming language with the ability to succinctly import all of the packages exported by a module, with the goal of simplifying the reuse of modular libraries without requiring code to be in a module itself.
This JEP streamlines the importing of entire modules in Java, thus simplifying code and making it easier for developers, especially beginners, to utilize libraries and standard classes. This feature reduces the need for multiple import statements and eliminates the necessity of knowing the package hierarchy.
Importantly, this change does not disrupt existing code, as developers are not required to modularize their work. This feature is developed in conjunction with JEP 477, which automatically imports all public classes and interfaces from the java.base
module for implicitly declared classes.
The Java programming language includes the automatic import of essential classes from the java.lang
package. However, as the platform has evolved, many classes, like List
, Map
, and Stream
are not automatically included, forcing developers to import them explicitly.
For instance, the following code demonstrates how manually importing several packages consumes unnecessary lines:
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
String[] fruits = new String[] { "apple", "berry", "citrus" };
Map<String, String> m =
Stream.of(fruits)
.collect(Collectors.toMap(s -> s.toUpperCase().substring(0,1),
Function.identity()));
With module imports, the syntax simplifies significantly:
import module java.base;
String[] fruits = new String[] { "apple", "berry", "citrus" };
Map<String, String> m =
Stream.of(fruits)
.collect(Collectors.toMap(s -> s.toUpperCase().substring(0,1),
Function.identity()));
A module import declaration follows this pattern:
import module M;
where M
is the name of the module whose packages should be imported.
The effect of the import module is twofold:
- Direct Packages: It imports all public top-level classes and interfaces in packages exported by the
module M
to the current module. - Transitive Dependencies: Packages exported by modules read via transitive dependencies are also imported.
As an example, the declaration import module java.base
imports all 54 exported packages, effectively bringing a wide range of classes into scope from java.util
to java.io
.
However, importing entire modules increases the risk of ambiguous names when multiple packages contain classes with identical simple names. For instance, this example will trigger an error due to ambiguous List
references:
import module java.base; // exports java.util.List
import module java.desktop; // exports java.awt.List
List l = ... // Error - Ambiguous name!
The solution is to import the desired type explicitly:
import java.sql.Date; // resolve the ambiguity of the simple name Date!
Date d = ... // Ok! Date is resolved to java.sql.Date
The import module feature is currently a preview available through the --enable-preview
flag with the JDK 23 compiler and runtime. To compile and run use the following command:
-
Compile the program with
javac --release 23 --enable-preview Main.java
and run it withjava --enable-preview Main
; or, -
When using the source code launcher, run the program with
java --enable-preview Main.java
; or, -
When using jshell, start it with jshell
--enable-preview
.
This JEP aims to provide a cleaner and more modular way to import Java libraries, reducing boilerplate code and enhancing accessibility, especially for new learners and developers working with modular libraries.
Overall, Java’s module import feature promises to improve productivity and ease of development. By simplifying imports, developers can focus more on crafting meaningful code and less on keeping their imports organized.