Relates :
|
|
Relates :
|
|
Relates :
|
JDK-8328481 :
|
|
JDK-8329080 :
|
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). 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. - 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 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/22/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/22/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/22/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/22/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/22/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 23, you must enable preview features: - Compile the program with `javac --release 23 --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 §7.5](https://docs.oracle.com/javase/specs/jls/se22/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. The source file need not be associated with an explicit module. For example, `java.base` and `java.sql` are part of the standard Java runtime, and can be imported by programs which are not themselves developed as modules. (For technical background, see [JEP 261](https://openjdk.org/jeps/261#Root-modules).) It is sometimes useful to import a module that does not export any packages, because the module transitively requires other modules that do export packages. For example, the [`java.se`](https://docs.oracle.com/en/java/javase/22/docs/api/java.se/module-summary.html) module does not export any packages, but it requires 19 other modules transitively, so the effect of `import module java.se` is to import the packages which are exported by those modules, and so on, recursively — specifically, the 123 packages listed as the [indirect exports of the `java.se` module](https://docs.oracle.com/en/java/javase/22/docs/api/java.se/module-summary.html#packages-summary). Note that `import module java.se` is only possible in a compilation unit of a named module that already `requires java.se`. In a compilation unit of the unnamed module, such as one that [implicitly declares](https://openjdk.org/jeps/477) a class, it is not possible to use `import module java.se`. ### Ambiguous imports Since importing a module has the effect of importing multiple packages, 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 a single-type-import declaration. For example, to resolve the ambiguous `Date` of the previous example: ``` 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 ... ``` ### A worked example Here is an example of how `import module` works. Suppose `C.java` is a source file associated with 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`. ### Implicitly declared classes This JEP is co-developed with [_JEP 477: Implicitly Declared Classes and Instance `main` Methods_](https://openjdk.org/jeps/477), which specifies that all `public` top level classes and interfaces in all packages exported by the `java.base` module are automatically imported on-demand in implicitly declared classes. In other words, it is as if `import module java.base` appears at the beginning of every such class, versus `import java.lang.*` at the beginning of every ordinary class. 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`. 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 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 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 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.