This document describes changes to the Java Language Specification to support Module Import Declarations, which is a preview feature of Java SE 23. See JEP 476 for an overview of the feature.
The preview feature Implicitly declared classes and instance
main
methods proposed by JEP 477 depends on this
feature.
Changes are described with respect to existing sections of the JLS.
New text is indicated like this and deleted text is
indicated like this. Explanation and discussion, as needed,
is set aside in grey boxes.
Changelog:
2024-06-04: Adding missing update to section 3.9
2024-05-07: Updated with JEP 477 link.
2024-04: First draft.
Chapter 3: Lexical Structure
3.9 Keywords
51 character sequences, formed from ASCII characters, are reserved for use as keywords and cannot be used as identifiers (3.8). Another 17 character sequences, also formed from ASCII characters, may be interpreted as keywords or as other tokens, depending on the context in which they appear.
- Keyword:
- ReservedKeyword
- ContextualKeyword
- ReservedKeyword:
-
(one of) #
abstract continue for new switch
assert default if package synchronized
boolean do goto private this
break double implements protected throw
byte else import public throws
case enum instanceof return transient
catch extends int short try
char final interface static void
class finally long strictfp volatile
const float native super while
_
(underscore) - ContextualKeyword:
-
(one of) #
exports opens requires uses yield
module permits sealed var
non-sealed provides to when
open record transitive with
The keywords
const
andgoto
are reserved, even though they are not currently used. This may allow a Java compiler to produce better error messages if these C++ keywords incorrectly appear in programs.
The keyword
strictfp
is obsolete and should not be used in new code.
The keyword
_
(underscore) may be used in certain declarations in place of an identifier (6.1).
true
andfalse
are not keywords, but rather boolean literals (3.10.3).
null
is not a keyword, but rather the null literal (3.10.8).
During the reduction of input characters to input elements (3.5), a sequence of input characters that notionally matches a contextual keyword is reduced to a contextual keyword if and only if both of the following conditions hold:
The sequence is recognized as a terminal specified in a suitable context of the syntactic grammar (2.3), as follows:
- For
module
andopen
, when recognized as a terminal in a ModuleDeclaration (7.7).
For
exports
,opens
,provides
,requires
,to
,uses
, andwith
, when recognized as a terminal in a ModuleDirective.For
transitive
, when recognized as a terminal in a RequiresModifier.For example, recognizing the sequence
requires
transitive
;
does not make use of RequiresModifier, so the termtransitive
is reduced here to an identifier and not a contextual keyword.For
var
, when recognized as a terminal in a LocalVariableType (14.4) or a LambdaParameterType (15.27.1).In other contexts, attempting to use
var
as an identifier will cause an error, becausevar
is not a TypeIdentifier (3.8).For
yield
, when recognized as a terminal in a YieldStatement (14.21).In other contexts, attempting to use the
yield
as an identifier will cause an error, becauseyield
is neither a TypeIdentifier nor a UnqualifiedMethodIdentifier.For
record
, when recognized as a terminal in a RecordDeclaration (8.10).For
non-sealed
,permits
, andsealed
, when recognized as a terminal in a NormalClassDeclaration (8.1) or a NormalInterfaceDeclaration (9.1).For
when
, when recognized as a terminal in a Guard (14.11.1).
- For
The sequence is not immediately preceded or immediately followed by an input character that matches JavaLetterOrDigit.
In general, accidentally omitting white space in source code will cause a sequence of input characters to be tokenized as an identifier, due to the "longest possible translation" rule (3.2). For example, the sequence of twelve input characters
p u b l i c s t a t i c
is always tokenized as the identifierpublicstatic
, rather than as the reserved keywordspublic
andstatic
. If two tokens are intended, they must be separated by white space or a comment.
The rule above works in tandem with the "longest possible translation" rule to produce an intuitive result in contexts where contextual keywords may appear. For example, the sequence of eleven input characters
v a r f i l e n a m e
is usually tokenized as the identifiervarfilename
, but in a local variable declaration, the first three input characters are tentatively recognized as the contextual keywordvar
by the first condition of the rule above. However, it would be confusing to overlook the lack of white space in the sequence by recognizing the next eight input characters as the identifierfilename
. (This would mean that the sequence undergoes different tokenization in different contexts: an identifier in most contexts, but a contextual keyword and an identifier in local variable declarations.) Accordingly, the second condition prevents recognition of the contextual keywordvar
on the grounds that the immediately following input characterf
is a JavaLetterOrDigit. The sequencev a r f i l e n a m e
is therefore tokenized as the identifiervarfilename
in a local variable declaration.
As another example of the careful recognition of contextual keywords, consider the sequence of 15 input characters
n o n - s e a l e d c l a s s
. This sequence is usually translated to three tokens - the identifiernon
, the operator-
, and the identifiersealedclass
- but in a normal class declaration, where the first condition holds, the first ten input characters are tentatively recognized as the contextual keywordnon-sealed
. To avoid translating the sequence to two keyword tokens (non-sealed
andclass
) rather than three non-keyword tokens, and to avoid rewarding the programmer for omitting white space beforeclass
, the second condition prevents recognition of the contextual keyword. The sequencen o n - s e a l e d c l a s s
is therefore tokenized as three tokens in a class declaration.
In the rule above, the first condition depends on details of the syntactic grammar, but a compiler for the Java programming language can implement the rule without fully parsing the input program. For example, a heuristic could be used to track the contextual state of the tokenizer, as long as the heuristic guarantees that valid uses of contextual keywords are tokenized as keywords, and valid uses of identifiers are tokenized as identifiers. Alternatively, a compiler could always tokenize a contextual keyword as an identifier, leaving it to a later phase to recognize special uses of these identifiers.
Chapter 6: Names
6.1 Declarations
A declaration introduces one of the following entities into a program:
A module, declared in a
module
declaration (7.7)A package, declared in a
package
declaration (7.4)An imported class or interface, declared in a single-type-import declaration,
ora type-import-on-demand declaration, or a single-module-import declaration (7.5.1, 7.5.2, 7.5.5)An imported
static
member, declared in a single-static-import declaration or a static-import-on-demand declaration (7.5.3, 7.5.4)A class, declared by a normal class declaration (8.1), an enum declaration (8.9), or a record declaration (8.10)
An interface, declared by a normal interface declaration (9.1) or an annotation interface declaration (9.6).
A type parameter, declared as part of the declaration of a generic class, interface, method, or constructor (8.1.2, 9.1.2, 8.4.4, 8.8.4)
A member of a reference type (8.2, 9.2, 8.9.3, 9.6, 10.7), one of the following:
A field, one of the following:
A method, one of the following:
An enum constant (8.9.1)
A record component (8.10.3)
A formal parameter, one of the following:
An exception parameter of an exception handler declared in a
catch
clause of atry
statement (14.20)A local variable, one of the following:
A local class or interface (14.3), one of the following:
A local class declared by a normal class declaration
A local class declared by an enum declaration
A local class declared by an record declaration
A local interface declared by a normal interface declaration
The rest of the section is unchanged.
6.3 Scope of a Declaration
The scope of a declaration is the region of the program within which the entity declared by the declaration can be referred to using a simple name, provided it is not shadowed (6.4.1).
A declaration is said to be in scope at a particular point in a program if and only if the declaration's scope includes that point.
The scope of the declaration of an observable top level package (7.4.3) is all observable compilation units associated with modules to which the package is uniquely visible (7.4.3).
The declaration of a package that is not observable is never in scope.
The declaration of a subpackage is never in scope.
The package java
is always in scope.
The scope of a class or interface imported by a single-type-import
declaration (7.5.1),
or a type-import-on-demand declaration (7.5.2),
or a single-module-import declaration (7.5.5) is the module declaration (7.7)
and all the class and interface declarations (8.1,
9.1)
of the compilation unit in which the import
declaration
appears, as well as any annotations on the module declaration or package
declaration of the compilation unit.
The scope of a member imported by a single-static-import declaration
(7.5.3)
or a static-import-on-demand declaration (7.5.4)
is the module declaration and all the class and interface declarations
of the compilation unit in which the import
declaration
appears, as well as any annotations on the module declaration or package
declaration of the compilation unit.
The scope of a top level class or interface (7.6) is all class and interface declarations in the package in which the top level class or interface is declared.
The scope of a declaration of a member m declared in or inherited by a class or interface C (8.2, 9.2) is the entire body of C, including any nested class or interface declarations. If C is a record class, then the scope of m additionally includes the header of the record declaration of C.
The scope of a formal parameter of a method (8.4.1), constructor (8.8.1), or lambda expression (15.27) is the entire body of the method, constructor, or lambda expression.
The scope of a class's type parameter (8.1.2) is the type parameter section of the class declaration, and the type parameter section of any superclass type or superinterface type of the class declaration, and the class body. If the class is a record class (8.10), then the scope of the type parameter additionally includes the header of the record declaration (8.10.1).
The scope of an interface's type parameter (9.1.2) is the type parameter section of the interface declaration, and the type parameter section of any superinterface type of the interface declaration, and the interface body.
The scope of a method's type parameter (8.4.4) is the entire declaration of the method, including the type parameter section, but excluding the method modifiers.
The scope of a constructor's type parameter (8.8.4) is the entire declaration of the constructor, including the type parameter section, but excluding the constructor modifiers.
The scope of a local class or interface declaration immediately enclosed by a block (14.2) is the rest of the immediately enclosing block, including the local class or interface declaration itself.
The scope of a local class or interface declaration immediately enclosed by a switch block statement group (14.11) is the rest of the immediately enclosing switch block statement group, including the local class or interface declaration itself.
The scope of a local variable declared in a block by a local variable declaration statement (14.4.2) is the rest of the block, starting with the declaration's own initializer and including any further declarators to the right in the local variable declaration statement.
The scope of a local variable declared in the ForInit part
of a basic for
statement (14.14.1)
includes all of the following:
Its own initializer
Any further declarators to the right in the ForInit part of the
for
statementThe Expression and ForUpdate parts of the
for
statementThe contained Statement
The scope of a local variable declared in the header of an enhanced
for
statement (14.14.2)
is the contained Statement.
The scope of a local variable declared in the resource specification
of a try
-with-resources statement (14.20.3)
is from the declaration rightward over the remainder of the resource
specification and the entire try
block associated with the
try
-with-resources statement.
The translation of a
try
-with-resources statement implies the rule above.
The scope of a parameter of an exception handler that is declared in
a catch
clause of a try
statement (14.20)
is the entire block associated with the catch
.
The rest of the section is unchanged.
6.4 Shadowing and Obscuring
6.4.1 Shadowing
Some declarations may be shadowed in part of their scope by another declaration of the same name, in which case a simple name cannot be used to refer to the declared entity.
Shadowing is distinct from hiding (8.3, 8.4.8.2, 8.5, 9.3, 9.5), which applies only to members which would otherwise be inherited but are not because of a declaration in a subclass. Shadowing is also distinct from obscuring (6.4.2).
A declaration d of a type named n shadows the declarations of any other types named n that are in scope at the point where d occurs throughout the scope of d.
A declaration d of a field or formal parameter named n shadows, throughout the scope of d, the declarations of any other variables named n that are in scope at the point where d occurs.
A declaration d of a local variable or exception parameter named n shadows, throughout the scope of d, (a) the declarations of any other fields named n that are in scope at the point where d occurs, and (b) the declarations of any other variables named n that are in scope at the point where d occurs but are not declared in the innermost class in which d is declared.
A declaration d of a method named n shadows the declarations of any other methods named n that are in an enclosing scope at the point where d occurs throughout the scope of d.
A package declaration never shadows any other declaration.
A type-import-on-demand declaration never causes any other declaration to be shadowed.
A static-import-on-demand declaration never causes any other declaration to be shadowed.
A single-module-import declaration never causes any other declaration to be shadowed.
A single-type-import declaration d in a compilation unit c of package p that imports a type named n shadows, throughout c, the declarations of:
any top level type named n declared in another compilation unit of p
any type named n imported by a type-import-on-demand declaration in c
any type named n imported by a static-import-on-demand declaration in c
any type named n imported by a single-module-import declaration in c
A single-static-import declaration d in a compilation unit c of package p that imports a field named n shadows the declaration of any static field named n imported by a static-import-on-demand declaration in c, throughout c.
A single-static-import declaration d in a compilation unit c of package p that imports a method named n with signature s shadows the declaration of any static method named n with signature s imported by a static-import-on-demand declaration in c, throughout c.
A single-static-import declaration d in a compilation unit c of package p that imports a type named n shadows, throughout c, the declarations of:
any static type named n imported by a static-import-on-demand declaration in c;
any top level type (7.6) named n declared in another compilation unit (7.3) of p;
any type named n imported by a type-import-on-demand declaration (7.5.2) in c.
any type named n imported by a single-module-import declaration in c.
The rest of the section is unchanged.
6.5 Determining the Meaning of a Name
6.5.1 Syntactic Classification of a Name According to Context
A name is syntactically classified as a ModuleName in these contexts:
In a
requires
directive in a module declaration (7.7.1)To the right of
to
in anexports
oropens
directive in a module declaration (7.7.2)To the right of
module
in a single-module-import declaration (7.5.5)
A name is syntactically classified as a PackageName in these contexts:
To the right of
exports
oropens
in a module declarationTo the left of the "
.
" in a qualified PackageName
A name is syntactically classified as a TypeName in these contexts:
To name a class or interface:
In a
uses
orprovides
directive in a module declaration (7.7.1)In a single-type-import declaration (7.5.1)
To the left of the
.
in a single-static-import declaration (7.5.3)To the left of the
.
in a static-import-on-demand declaration (7.5.4)In a
permits
clause of asealed
class or interface declaration (8.1.6, 9.1.4).To the left of the
(
in a constructor declaration (8.8)After the
@
sign in an annotation (9.7)To the left of
.class
in a class literal (15.8.2)To the left of
.this
in a qualifiedthis
expression (15.8.4)To the left of
.super
in a qualified superclass field access expression (15.11.2)To the left of
.
Identifier or.super.
Identifier in a qualified method invocation expression (15.12)To the left of
.super::
in a method reference expression (15.13)
As the Identifier or dotted Identifier sequence that constitutes any ReferenceType (including a ReferenceType to the left of the brackets in an array type, or to the left of the < in a parameterized type, or in a non-wildcard type argument of a parameterized type, or in an
extends
orsuper
clause of a wildcard type argument of a parameterized type) in the 17 contexts where types are used (4.11):In an
extends
orimplements
clause of a class declaration (8.1.4, 8.1.5)In an
extends
clause of an interface declaration (9.1.3)The return type of a method (8.4.5, 9.4), including the type of an element of an annotation interface (9.6.1)
In the
throws
clause of a method or constructor (8.4.6, 8.8.5, 9.4)In an
extends
clause of a type parameter declaration of a generic class, interface, method, or constructor (8.1.2, 9.1.2, 8.4.4, 8.8.4)The type in a field declaration of a class or interface (8.3, 9.3)
The type in a formal parameter declaration of a method, constructor, or lambda expression (8.4.1, 8.8.1, 9.4, 15.27.1)
The type of the receiver parameter of a method (8.4)
The type in a local variable declaration in either a statement (14.4.2, 14.14.1, 14.14.2, 14.20.3) or a pattern (14.30.1)
A type in an exception parameter declaration (14.20)
The type in a record component declaration of a record class (8.10.1)
In an explicit type argument list to an explicit constructor invocation statement or class instance creation expression or method invocation expression (8.8.7.1, 15.9, 15.12)
In an unqualified class instance creation expression, either as the class type to be instantiated (15.9) or as the direct superclass or direct superinterface of an anonymous class to be instantiated (15.9.5)
The element type in an array creation expression (15.10.1)
The type in the cast operator of a cast expression (15.16)
The type that follows the
instanceof
relational operator (15.20.2)In a method reference expression (15.13), as the reference type to search for a member method or as the class type or array type to construct.
The extraction of a TypeName from the identifiers of a ReferenceType in the 17 contexts above is intended to apply recursively to all sub-terms of the ReferenceType, such as its element type and any type arguments.
For example, suppose a field declaration uses the type
p.q.Foo[]
. The brackets of the array type are ignored, and the termp.q.Foo
is extracted as a dotted sequence of Identifiers to the left of the brackets in an array type, and classified as a TypeName. A later step determines which ofp
,q
, andFoo
is a type name or a package name.As another example, suppose a cast operator uses the type
p.q.Foo<? extends String>
. The termp.q.Foo
is again extracted as a dotted sequence of Identifier terms, this time to the left of the<
in a parameterized type, and classified as a TypeName. The termString
is extracted as an Identifier in anextends
clause of a wildcard type argument of a parameterized type, and classified as a TypeName.
A name is syntactically classified as an ExpressionName in these contexts:
As the qualifying expression in a qualified superclass constructor invocation (8.8.7.1)
As the qualifying expression in a qualified class instance creation expression (15.9)
As the array reference expression in an array access expression (15.10.3)
As a PostfixExpression (15.14)
As the left-hand operand of an assignment operator (15.26)
As a VariableAccess in a
try
-with-resources statement (14.20.3)
A name is syntactically classified as a MethodName in this context:
- Before the "
(
" in a method invocation expression (15.12)
A name is syntactically classified as a PackageOrTypeName in these contexts:
To the left of the "
.
" in a qualified TypeNameIn a type-import-on-demand declaration (7.5.2)
A name is syntactically classified as an AmbiguousName in these contexts:
To the left of the "
.
" in a qualified ExpressionNameTo the left of the rightmost
.
that occurs before the "(
" in a method invocation expressionTo the left of the "
.
" in a qualified AmbiguousNameIn the default value clause of an annotation element declaration (9.6.2)
To the right of an "
=
" in an element-value pair (9.7.1)To the left of
::
in a method reference expression (15.13)
The effect of syntactic classification is to restrict certain kinds of entities to certain parts of expressions:
The name of a field, parameter, or local variable may be used as an expression (15.14.1).
The name of a method may appear in an expression only as part of a method invocation expression (15.12).
The name of a class or interface may appear in an expression only as part of a class literal (15.8.2), a qualified
this
expression (15.8.4), a class instance creation expression (15.9), an array creation expression (15.10.1), a cast expression (15.16), aninstanceof
expression (15.20.2), an enum constant (8.9), or as part of a qualified name for a field or method.The name of a package may appear in an expression only as part of a qualified name for a class or interface.
Chapter 7: Packages and Modules
7.5 Import Declarations
An import declaration allows a named class, interface, or
static
member to be referred to by a simple name (6.2)
that consists of a single identifier.
Without the use of an appropriate import declaration, a reference to
a class or interface declared in another package, or a reference to a
static
member of another class or interface, would
typically need to use a fully qualified name (6.7).
- ImportDeclaration:
- SingleTypeImportDeclaration
- TypeImportOnDemandDeclaration
- SingleStaticImportDeclaration
- StaticImportOnDemandDeclaration
- SingleModuleImportDeclaration
A single-type-import declaration (7.5.1) imports a single named class or interface, by mentioning its canonical name (6.7).
A type-import-on-demand declaration (7.5.2) imports all the accessible classes and interfaces of a named package, class, or interface as needed, by mentioning the canonical name of the package, class, or interface.
A single-static-import declaration (7.5.3) imports all accessible
static
members with a given name from a class or interface, by giving its canonical name.A static-import-on-demand declaration (7.5.4) imports all accessible
static
members of a named class or interface as needed, by mentioning the canonical name of the class or interface.A single-module-import declaration (7.5.5) imports all the accessible classes and interfaces of the packages exported by a given module, as needed.
The scope and shadowing of a class, interface, or member imported by these declarations is specified in 6.3 and 6.4.
An
import
declaration makes classes, interfaces, or members available by their simple names only within the compilation unit that actually contains theimport
declaration. The scope of the class(es), interface(s), or member(s) introduced by animport
declaration specifically does not include other compilation units in the same package, otherimport
declarations in the current compilation unit, or apackage
declaration in the current compilation unit (except for the annotations of apackage
declaration).
7.5.5 Single-Module-Import Declarations
A single-module-import declaration allows all
public
top level classes and interfaces of the packages
exported by a named module to be imported as needed.
- SingleModuleImportDeclaration:
-
import
module
ModuleName;
A single-module-import declaration import module M;
imports, on demand, all the public
top level classes and
interfaces in the following packages:
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 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 those other modules.
It is a compile-time error if the module ModuleName is not read by the current module (7.3).
The modules read by the current module are given by the result of resolution, as described in the
java.lang.module
package specification (7.3).
Two or more single-module-import declarations in the same compilation unit may name the same module. All but one of these declarations are considered redundant; the effect is as if that module was imported only once.
A single-module-import declaration can be used in any source file. It is not required for the source file to be part of a module. For example, modules
java.base
andjava.sql
are part of the standard Java runtime, so they can be imported by programs which are not themselves developed as modules.It is sometimes useful to import a module that does not export any packages. This is because the module may transitively require other modules that do export packages. For example, the
java.se
module does not export any packages, but it requires a number of modules transitively, so the effect of the single-module-import declarationimport module java.se;
is to import the packages which are exported by those modules (and so on, recursively).
Example 7.5.5-1. Single-Module-Import in Ordinary Compilation Units
Modules allow a set of packages to be grouped together for reuse under a single name, and the exported packages of a module are intended to form a cohesive and coherent API. Single-module-import declarations allow the developer to import all the packages exported by a module in one go, simplifying the reuse of modular libraries. For example:
import module java.xml;
causes the simple names of the public
top level classes
and interfaces declared in all packages exported by module
java.xml
to be available within the class and interface
declarations of the compilation unit. Thus, the simple name
XPath
refers to the interface XPath
of the
package javax.xml.xpath
exported by the module
java.xml
in all places in the compilation unit where that
class declaration is not shadowed or obscured.
Assume the following compilation unit associated with module
M0
:
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 the single-module-import declaration
import module M1;
depends on the exports of M1
and any modules that M1
requires transitively. Consider as
an example:
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 the single-module-import declaration
import module M1;
is then:
Import the
public
top level classes and interfaces from packagep1
, sinceM1
exportsp1
to everyone;Import the
public
top level classes and interfaces from packagep2
, sinceM1
exportsp2
toM0
, the module with which the compilation unit is associated; andImport the
public
top level classes and interfaces from packagep10
, sinceM1
requires transitivelyM4
, which exportsp10
.
Nothing from packages p3
or p11
is imported
by the compilation unit.
Single-module-import declarations may appear in a source file
containing only a package declaration. Such files are typically called
package-info.java
and are used as the sole repository for
package-level annotations and documentation (7.4.1).
Example 7.5.5-2. Single-Module-Import in Modular Compilation Units
Import declarations can also appear in a modular compilation unit.
The following modular compilation unit uses a single-module-import
declaration, allowing the simple name of the interface
Driver
associated with module java.sql
to be
used in the provides
directive:
import module java.sql;
module com.myDB.core {
exports ...
requires transitive java.sql;
provides Driver with com.myDB.greatDriver;
}
It is possible for a modular compilation unit that declares a module
M
to also import the module M
. In the
following example, this means that the simple name of a class
C
can be used in a uses
directive:
import module M;
module M {
...
exports p;
...
uses C;
...
}
where the package p
exported by module M
is
declared as follows:
package p;
class C { ... }
Without the single-module-import declaration, the qualified name of
the class C
would need to be used in the uses
directive.
Suppose a module declaration as follows:
module M2 {
requires java.se;
exports p2;
...
}
where the package p2
exported by M2
is
declared as follows:
package p2;
import module java.xml;
class MyClass {
...
}
Even though the module M2
does not directly express a
dependency on the module java.xml
, the import of module
java.xml
is still correct as the resolution process will
determine that the module java.xml
is read by module
M2
.
Example 7.5.5-3. Ambiguous Imports
Clearly importing multiple modules could lead to name ambiguities, for example:
import module java.base;
import module java.desktop;
...
List l = ... // Error - Ambiguous name!
...
The module java.base
exports the package
java.util
, which has a public
List
interface. The module java.desktop
exports the package java.awt
, which a public
List
class. Having imported both modules, the use of the
simple name List
is clearly ambiguous and results in a
compile-time error.
However, just importing a single module can also lead to a name ambiguity, for example:
import module java.desktop;
...
Element e = ... // Error - Ambiguous name!
...
The module java.desktop
exports packages,
javax.swing.text
and
javax.swing.text.html.parser
, which have a
public
Element
interface and a
public
Element
class, respectively. Thus the
use of the simple name Element
is ambiguous and results in
a compile-time error.
A single-type-import declaration can be used to resolve a name
ambiguity. The earlier example where the simple name List
is ambiguous can be resolved as follows:
import module java.base;
import module java.desktop;
import java.util.List; // Resolving the ambiguity of the simple name List
...
List l = ... // Ok - List is resolved to java.util.List
...