JDK-8335987 : JEP 494: Module Import Declarations (Second Preview)
  • Type: JEP
  • Component: specification
  • Sub-Component: language
  • Priority: P4
  • Status: Proposed to Target
  • Resolution: Unresolved
  • Fix Versions: 24
  • Submitted: 2024-07-09
  • Updated: 2024-11-06
Related Reports
Relates :  
Relates :  
Sub Tasks
JDK-8335989 :  
JDK-8335990 :  
Description
Summary
-------

Enhance the Java programming language with the ability to succinctly import all
of the packages exported by a module. This simplifies the reuse of modular
libraries, but does not require the importing code to be in a module itself.
This is a [preview language feature](https://openjdk.org/jeps/12).

History
-------

Module import declarations were first proposed as a preview feature by
[JEP 476](https://openjdk.org/jeps/476) (JDK 23). We here
propose to preview them for a second time to gain more experience and
feedback, with two additions:

-   Lift the restriction that no module is able to declare a transitive
    dependence on the `java.base` module, and revise the declaration of
    the `java.se` module to transitively require the `java.base` module.
    With these changes, importing the `java.se` module will import the
    entire Java SE API on demand.

-   Allow type-import-on-demand declarations to shadow module import
    declarations.



Goals
-----

-   Simplify the reuse of modular libraries by allowing entire modules to be
    imported at once.

-   Avoid the noise of multiple type-import-on-demand declarations (e.g.,
    `import com.foo.bar.*`) when using diverse parts of the API exported by a
    module.

-   Allow beginners to more easily use third-party libraries and fundamental
    Java classes without having to learn where they are located in a package
    hierarchy.

-   Ensure that module import declarations work smoothly alongside
    existing import declarations.

-   Do not require developers who use the module import feature to modularize
    their own code.


Motivation
----------

Classes and interfaces in the `java.lang` package, such as `Object`, `String`,
and `Comparable`, are essential to every Java program. For this reason, the Java
compiler automatically imports, on demand, all of the classes and interfaces in
the `java.lang` package, as if

```
import java.lang.*;
```

appears at the beginning of every source file.

As the Java Platform has evolved, classes and interfaces such as `List`, `Map`,
`Stream`, and `Path` have become almost as essential. However, none of these are
in `java.lang`, so they are not automatically imported; rather, developers have
to keep the compiler happy by writing a plethora of `import` declarations at the
beginning of every source file. For example, the following code converts an
array of strings into a map from capital letters to strings, but the imports
take almost as many lines as the code:

```
import java.util.Map;                   // or import java.util.*;
import java.util.function.Function;     // or import java.util.function.*;
import java.util.stream.Collectors;     // or import java.util.stream.*;
import java.util.stream.Stream;         // (can be removed)

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()));
```

Developers have diverse views as to whether to prefer single-type-import or
type-import-on-demand declarations. Many
[prefer](https://google.github.io/styleguide/javaguide.html#s3.3-import-statements)
single-type imports in large, mature codebases where clarity is paramount.
However, in early-stage situations where convenience trumps clarity, developers
often prefer on-demand imports; for example,

  - When prototyping code and [running it with the `java`
    launcher](https://openjdk.org/jeps/458);

  - When exploring a new API in [JShell](https://openjdk.org/jeps/222), such as
    [Stream Gatherers](https://openjdk.org/jeps/473) or the [Foreign Function &
    Memory API](https://openjdk.org/jeps/454); or

  - When learning to program with new features that work in concert with new
    APIs, such as [virtual threads and their
    executors](https://openjdk.org/jeps/444#Using-virtual-threads-vs--platform-threads).

Since Java 9, modules have allowed a set of packages to be grouped together for
reuse under a single name. The exported packages of a module are intended to
form a cohesive and coherent API, so it would be convenient if developers could
import on-demand from the entire module, that is, from all of the packages
exported by the module. It would be as if all the exported packages are imported
in one go.

For example, importing the `java.base` module on-demand would give immediate
access to `List`, `Map`, `Stream`, and `Path`, without having to manually import
`java.util` on-demand, and `java.util.stream` on-demand, and `java.nio.file`
on-demand.

The ability to import at the level of modules would be especially helpful when
APIs in one module have a close relationship with APIs in another module. This
is common in large multi-module libraries such as the JDK. For example, the
[`java.sql`](https://docs.oracle.com/en/java/javase/23/docs/api/java.sql/module-summary.html)
module provides database access via its `java.sql` and `javax.sql` packages, but
one of its interfaces,
[`java.sql.SQLXML`](https://docs.oracle.com/en/java/javase/23/docs/api/java.sql/java/sql/SQLXML.html),
declares `public` methods whose signatures use interfaces from the
`javax.xml.transform` package in the
[`java.xml`](https://docs.oracle.com/en/java/javase/23/docs/api/java.xml/module-summary.html)
module. Developers who call these methods in `java.sql.SQLXML` typically import
both the `java.sql` package and the `javax.xml.transform` package.  To
facilitate this extra import, the `java.sql` module depends on the `java.xml`
module [transitively](https://dev.java/learn/modules/implied-readability/), so
that a program which depends on the `java.sql` module depends automatically on
the `java.xml` module. In this scenario, it would be convenient if importing the
`java.sql` module on-demand would also automatically import the `java.xml`
module on-demand. Automatically importing on-demand from transitive dependencies
would be a further convenience when prototyping and exploring.


Description
-----------

A _module import declaration_ has the form

```
import module M;
```

It imports, on demand, all of the `public` top-level classes and interfaces in

  - The packages exported by the module `M` to the current module, and

  - The packages exported by the modules that are read by the current module due
    to reading the module `M`.

The second clause allows a program to use the API of a module, which might refer
to classes and interfaces from other modules, without having to import all of
those other modules.

For example:

  - `import module java.base` has the same effect as 54 on-demand package
    imports, one for each of the
    [packages](https://docs.oracle.com/en/java/javase/23/docs/api/java.base/module-summary.html)
    exported by the `java.base` module. It is as if the source file contains
    `import java.io.*` and `import java.util.*` and so on.

  - `import module java.sql` has the same effect as `import java.sql.*` and
    `import javax.sql.*` plus on-demand package imports for the [indirect
    exports of the `java.sql`
    module](https://docs.oracle.com/en/java/javase/23/docs/api/java.sql/module-summary.html#packages-summary).


### This is a [preview language feature](https://openjdk.org/jeps/12), disabled by default

To try the examples below in JDK 24, you must enable preview features:

- Compile the program with `javac --release 24 --enable-preview Main.java` and
  run it with `java --enable-preview Main`; or,

- When using the [source code launcher](https://openjdk.org/jeps/330), run the
  program with `java --enable-preview Main.java`; or,

- When using [`jshell`](https://openjdk.java.net/jeps/222), start it with
  `jshell --enable-preview`.


### Syntax and semantics

We extend the grammar of import declarations
([JLS&nbsp;§7.5](https://docs.oracle.com/javase/specs/jls/se23/html/jls-7.html#jls-7.5))
to include `import module` clauses:

```
ImportDeclaration:
  SingleTypeImportDeclaration
  TypeImportOnDemandDeclaration
  SingleStaticImportDeclaration
  StaticImportOnDemandDeclaration
  ModuleImportDeclaration

ModuleImportDeclaration:
  import module ModuleName;
```

`import module` takes a module name, so it is not possible to import packages
from the unnamed module, i.e., from the class path. This aligns with `requires`
clauses in module declarations, i.e., `module-info.java` files, which take
module names and cannot express a dependence on the unnamed module.

`import module` can be used in any source file, whether or not that file is part
of the definition of an explicit module. For example, `java.base` and `java.sql`
are part of the standard Java runtime, and can be imported by classes which are
only ever deployed on the class path. (For technical background, see
[JEP&nbsp;261](https://openjdk.org/jeps/261#Root-modules).)

Within a source file that is part of the definition of an explicit module,
`import module` can be used to conveniently import all of the packages that are
exported, without qualification, by that module. In such a source file, packages
in the module that are either not exported or exported with qualification must
continue to be imported in the traditional way. (In other words, `import module
M` is not more powerful for code inside module `M` than for code outside `M`.)

A source file may import the same module more than once.


### Resolving ambiguous imports

Importing a module has the effect of importing multiple packages, so it is
possible to import classes with the same simple name from different packages.
The simple name is ambiguous, so using it will cause a compile-time error.

For example, in this source file the simple name `Element` is ambiguous:

```
import module java.desktop;   // exports javax.swing.text,
                              // which has a public Element interface,
                              // and also exports javax.swing.text.html.parser,
                              // which has a public Element class

...
Element e = ...               // Error - Ambiguous name!
...
```

As another example, in this source file the simple name `List` is ambiguous:

```
import module java.base;      // exports java.util, which has a public List interface
import module java.desktop;   // exports java.awt, which a public List class

...
List l = ...                  // Error - Ambiguous name!
...
```

As a final example, in this source file the simple name `Date` is ambiguous:

```
import module java.base;      // exports java.util, which has a public Date class
import module java.sql;       // exports java.sql, which has a public Date class

...
Date d = ...                  // Error - Ambiguous name!
...
```

Resolving ambiguities is straightforward: Use another import declaration. For
example, adding a single-type-import declaration resolves the ambiguous `Date`
of the previous example by _shadowing_ the `Date` classes imported by the
`import module` declarations:

```
import module java.base;      // exports java.util, which has a public Date class
import module java.sql;       // exports java.sql, which has a public Date class

import java.sql.Date;         // resolve the ambiguity of the simple name Date!

...
Date d = ...                  // Ok!  Date is resolved to java.sql.Date
...
```

In other cases, it is more convenient to add an on-demand declaration to resolve
ambiguities by shadowing all of the classes in a package:

```
import module java.base;
import module java.desktop;
import java.util.*;
import javax.swing.text.*;

...
Element e = ...     // Element is resolved to javax.swing.text.Element
List l = ...        // List is resolved to java.util.List
Document d = ...    // Document is resolved to javax.swing.text.Document,
                    // regardless of any module imports
...
```

The shadowing behavior of import declarations matches their specificity. The
most specific, i.e., single-type-import declarations, can shadow both on-demand
and module import declarations, which are less specific. On-demand declarations
can shadow module import declarations, which are less specific, but not
single-type-import declarations, which are more specific.


### Coalescing import declarations

You may be able to coalesce multiple on-demand declarations into a single module
import declaration; for example:

```
import javax.xml.*;
import javax.xml.parsers.*;
import javax.xml.stream.*;
```

could be replaced with:

```
import module java.xml;
```

which is easier to read.


### Grouping import declarations

If a source file has a mix of different kinds of import declarations then
grouping them by kind may further improve readability; for example:

```
// Module imports
import module M1;
import module M2;
...

// Package imports
import P1.*;
import P2.*;
...

// Single-type imports
import P1.C1;
import P2.C2;
...

class Foo { ... }
```

The order of the groups reflects their shadowing behavior: The least-specific
module import declarations are first, the most-specific single-type imports are
last, and the on-demand imports are in between.


### A worked example

Here is an example of how `import module` works. Suppose the source file
`C.java` is part of the definition of module `M0`:

```
// C.java
package q;
import module M1;             // What does this import?
class C { ... }
```

where module `M0` has the following declaration:

```
module M0 { requires M1; }
```

The meaning of `import module M1` depends on the exports of `M1` and any modules
that `M1` requires transitively.

```
module M1 {
    exports p1;
    exports p2 to M0;
    exports p3 to M3;
    requires transitive M4;
    requires M5;
}

module M3 { ... }

module M4 { exports p10; }

module M5 { exports p11; }
```

The effect of `import module M1` is to

  - Import the `public` top level classes and interfaces from package `p1`,
    since `M1` exports `p1` to everyone;

  - Import the `public` top level classes and interfaces from package `p2`,
    since `M1` exports `p2` to `M0`, the module with which `C.java` is
    associated; and

  - Import the `public` top level classes and interfaces from package `p10`,
    since `M1` requires transitively `M4`, which exports `p10`.

Nothing from packages `p3` or `p11` is imported by `C.java`.


### Importing modules in simple source files

This JEP is co-developed with the JEP [_Simple Source Files and Instance `main`
Methods_](https://openjdk.org/jeps/8335984), which specifies that every `public`
top level class and interface in every package exported by the `java.base`
module is automatically imported on-demand in a simple source file. In other
words, it is as if `import module java.base` appears at the beginning of every
such file. A simple source file may import other modules, e.g., `java.desktop`,
and may explicitly import the `java.base` module even though doing so is
redundant.

The [JShell](https://openjdk.org/jeps/222) tool automatically imports ten
packages on-demand. The list of packages is ad-hoc. We therefore propose to
change JShell to automatically `import module java.base`.


### Importing aggregator modules

It is sometimes useful to import an _aggregator module_, i.e., a module which
does not export any packages itself but does export the packages exported by the
modules it requires. For example, the
[`java.se`](https://docs.oracle.com/en/java/javase/23/docs/api/java.se/module-summary.html)
module does not export any packages, but it requires nineteen other modules
[transitively](https://dev.java/learn/modules/implied-readability/), so the
effect of `import module java.se` is to import the packages exported by those
modules, and so on, recursively — specifically, the 123 packages listed as the
[indirect&nbsp;exports](https://docs.oracle.com/en/java/javase/23/docs/api/java.se/module-summary.html#packages-summary)
of the `java.se` module.

In earlier previews of this feature, developers were surprised to see that
importing the `java.se` module did not have the effect of importing the
`java.base` module. They thus had to either import the java.base module as well,
or else import specific packages from `java.base`, e.g., `import java.util.*`.

Importing the `java.se` module did not import the `java.base` module because the
Java Language Specification [explicitly
forbade](https://docs.oracle.com/javase/specs/jls/se23/html/jls-7.html#jls-7.7.1)
any module from declaring a transitive dependence on the `java.base` module.
This restriction was sensible in the original design of the modules feature,
since every module has an implicit dependence on `java.base`. With the module
import feature, however, which uses module declarations to derive a set of
packages to be imported, the ability to require `java.base` transitively is
useful.

We therefore propose to lift this language restriction. We will also revise the
declaration of the `java.se` module so that it transitively requires the
`java.base` module. Thus a single `import module java.se` is all that is needed
to use the entire standard Java API, no matter how many modules take part in
exporting the API.

Only aggregator modules in the Java Platform should use `requires transitive
java.base`. The clients of such aggregators expect all `java.*` modules to be
imported, including `java.base`. Modules in the Java Platform that have both
direct exports and indirect exports are not, strictly speaking, aggregators.
Hence they should not use `requires transitive java.base` because it may pollute
the client's namespace. For example, the `java.sql` module exports its own
packages as well as packages from `java.xml` and others, but a client that says
`import module java.sql` is not necessarily interested in importing everything
from `java.base`.

The directive `import module java.se` works only in source files that are part
of the definition of an explicit module that already `requires java.se`. In a
source file outside of a module definition, and in particular in a simple source
file which implicitly declares a class, using `import module java.se` fails
because `java.se` is not in the default set of root modules for the unnamed
module. In a source file that is part of the definition of an automatic module,
`import module java.se` will work if some other resolved explicit module
`requires java.se`.


Alternatives
------------

  - An alternative to `import module ...` is to automatically import more
    packages than just `java.lang`. This would bring more classes into scope,
    i.e., usable by their simple names, and delay the need for beginners to
    learn about imports of any kind. But, which additional packages should we
    import automatically?

    Every reader will have suggestions for which packages to auto-import from
    the omnipresent `java.base` module: `java.io` and `java.util` would be
    near-universal suggestions; `java.util.stream` and `java.util.function`
    would be common; and `java.math`, `java.net`, and `java.time` would each
    have supporters. For the JShell tool, we managed to find ten `java.*`
    packages which are broadly useful when experimenting with one-off Java code,
    but it is difficult to see which subset of `java.*` packages deserves to be
    permanently and automatically imported into every Java program. The list
    would, moreover, change change as the Java Platform evolves; e.g.,
    `java.util.stream` and `java.util.function` were introduced only in
    Java&nbsp;8.  Developers would likely become reliant on IDEs to remind them
    of which automatic imports are in effect — an undesirable outcome.

  - An important use case for this feature is to automatically import on-demand
    from the `java.base` module in implicitly declared classes. This could
    alternatively be achieved by automatically importing the&nbsp;54 packages
    exported by `java.base`. However, when an implicit class is migrated to an
    ordinary explicit class, which is the expected lifecycle, the developer
    would either have to write&nbsp;54 on-demand package imports, or else figure
    out which imports are necessary.


Risks and Assumptions
---------------------

Using one or more module import declarations leads to a risk of name ambiguity
due to different packages declaring members with the same simple name. This
ambiguity is not detected until the ambiguous simple name is used in a program,
when a compile-time error will occur. The ambiguity can be resolved by adding a
single-type-import declaration, but managing and resolving such name ambiguities
could be burdensome and lead to code that is brittle and difficult to read and
maintain.