4 Module Import Declarations
You can import all packages exported by a module with one declaration.
Note:
This is a preview feature. A preview feature is a feature whose design, specification, and implementation are complete, but is not permanent. A preview feature may exist in a different form or not at all in future Java SE releases. To compile and run code that contains preview features, you must specify additional command-line options. See Preview Language and VM Features. For more information about module import declarations, see JEP 494Consider the following example, which imports four classes:
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class FruitMap {
public static void main(String[] args) {
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()));
m.forEach((k, v) ->
System.out.println(k + " " + v));
}
}
You can replace the four single-type-import declarations in this example with type-import-on-demand declarations. However, you still need three of them:
import java.util.*;
import java.util.function.*;
import java.util.stream.*;
Because the module java.base exports the packages java.util, java.util.function and java.util.stream, you can replace these three declarations with one module import declaration:
import module java.base;
A module import declaration has the following form:
import module M;
It imports, on demand, all of the public top-level class and interfaces in the following:
-
The packages exported by the module
M
to the current module. -
The packages exported by the modules that are read by the current module due to reading the module
M
. This enables a program to use the API of a module, which might refer to classes and interfaces from other modules, without having to import all those other modules.For example, the module import declaration
import module java.sql
has the same effect asimport java.sql.*
plus on-demand imports for the indirect exports of the java.sql module, which include the packages java.logging and java.xml.
Ambiguous Imports
It's possible to import classes with the same simple name from different packages with module import declarations. However, this can lead to compile-time errors. The following example uses both java.awt.List and java.util.List:
import java.awt.Frame;
import java.awt.Label;
import java.awt.List;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.Arrays;
public class FruitApp {
FruitApp(java.util.List<String> fruits) {
Frame f = new Frame();
Label l = new Label("Fruits");
List lst = new List();
fruits.forEach(i -> lst.add(i));
l.setBounds(20, 40, 80, 30);
lst.setBounds(20, 70, 80, 80);
f.add(l);
f.add(lst);
f.setSize(200,200);
f.setTitle("Fruit");
f.setLayout(null);
f.setVisible(true);
f.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent we) {
System.exit(0);
}
});
}
public static void main(String args[]) {
String[] fruits = new String[] { "apple", "berry", "citrus" };
FruitApp f = new FruitApp(Arrays.asList(fruits));
}
}
Suppose you replace the single-type-import declarations with these module import declarations:
import module java.desktop;
import module java.base;
You'll get these compile-time errors:
FruitApp.java:8: error: reference to Label is ambiguous
Label l = new Label("Fruits");
^
both interface java.lang.classfile.Label in java.lang.classfile and class java.awt.Label in java.awt match
FruitApp.java:8: error: reference to Label is ambiguous
Label l = new Label("Fruits");
^
both interface java.lang.classfile.Label in java.lang.classfile and class java.awt.Label in java.awt match
FruitApp.java:9: error: reference to List is ambiguous
List lst = new List();
^
both interface java.util.List in java.util and class java.awt.List in java.awt match
FruitApp.java:9: error: reference to List is ambiguous
List lst = new List();
^
both interface java.util.List in java.util and class java.awt.List in java.awt match
The simple name Label is ambiguous. It's contained in java.lang.classfile and java.awt, which the modules java.base and java.desktop export, respectively. This issue applies similarly to the simple name List.
If a declaration of a type (such as a member variable or a parameter name) in a particular scope (such as an inner class or a method definition) has the same name as another declaration in the enclosing scope, then the declaration shadows the declaration of the enclosing scope
To resolve these ambiguities, use single-type-import declarations to
specify the canonical names of the simple names used in your code. These
declarations shadow the Label and List
classes imported by the import module
declarations.
import module java.desktop;
import module java.base;
import java.awt.Label;
import java.awt.List;
A declaration shadows another declaration when the shadowing declaration has the same name as another declaration in an enclosing scope. You can refer to a declaration that shadows by its simple name but not the declaration that it shadows. As demonstrated in this example, shadowing can also occur with single-type-import declarations.
Type-import-on-demand declarations can also shadow import module
declarations. For example, you can reduce the number of import statements in the
FruitApp
example by using a type-import-on-demand
declarations:
import module java.desktop;
import module java.base;
import java.awt.*;
Implicitly Declared Classes and the java.base Module
Every implicitly declared class (see Creating Simple Source Files) automatically imports, on demand, all public top-level classes
and interfaces in all packages exported by the java.base module. It's as if the module import declaration
import module java.base
appears at the beginning of every
implicitly declared class instead of the type-import-on-demand declaration
import java.lang.*
at the beginning of every ordinary class.
For example, you can simplify the FruitMap
example with an
implicitly declared class as follows:
void main() {
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()));
m.forEach((k, v) ->
System.out.println(k + " " + v));
}
Importing the java.se Module and Declaring a Transitive Dependence on the java.base Module
You can import the java.se module to import the entire standard
Java API, including all the packages contained in the java.base
module. Consequently, you can reduce the number of import statements even further in
the FruitApp
example:
import module java.se;
import java.awt.*;
With this updated list of import statements, compile the FruitApp
with the --add-modules java.se
option. The
java.se module is not part of the default set of root modules
for the unnamed module.
Prior to JDK 24, the java.se module didn't contain a transitive dependence on the java.base module. No module could have a transitive dependence on java.base because every module has an implicit dependence on it. However, because module import declarations derive a set of packages to be imported, you can now require the java.base module transitively.