-
Enhancement
-
Resolution: Fixed
-
P4
-
None
-
None
-
b25
https://docs.oracle.com/javase/specs/jls/se23/preview/specs/module-import-declarations-jls.html
says this:
> A type-import-on-demand declaration never causes any other declaration to be shadowed.
And there are examples later on which enforce this, showing that only a single-type-import declaration is capable of resolving a name ambiguity set up by a single-module-import declaration.
This is sub-optimal, because it makes ineffective an on-demand import of multiple names. This is a very popular feature of the language, and if users are forced to choose between module-import-on-demand and package-import-on-demand, they not adopt the new feature. Why would they be forced to choose? Because the ambiguity effects of a broad module import would be resolvable, not at the granularity of packages, but rather via the fine-grained (and therefore laborious) enumeration of one import per ambiguous.
Perhaps this sub-optimaility occurs (in practice) rarely enough that we don't care. And yet the following simple change (replacing the line above) would remove the problem permanently.
> A type-import-on-demand declaration d in a compilation unit c of package p that imports a type named n (from p) shadows, throughout c, the declarations of any type named n imported by a single-module-import declaration in c.
This can be explained by saying that, just as a more specific single-type-import declaration shadows any conflicting type-import-on-demand declaration (because it is less specific), a type-import-on-demand declaration is in turn more specific than any conflicting single-module-import declaration.
The existing rules turn a single-module-import declaration into an effectual "macro" that expands into many type-import-on-demand declarations, but surely the existence of an explicit, user-written type-import-on-demand declaration (just like an explicit single-type-import declaration) should overshadow any implicitly "macro-expanded" declaration.
The examples with List and Element in the narrative further down can be adjusted to suggest either kind of non-module import as a way to fix the problem shown.
To continue with previous examples of ambiguity, suppose the program has module imports that produce an ambiguity on List and/or Element:
import module java.base;
import module java.desktop;
Element e = ... // Error - Ambiguous name from two packages in one module!
List l = ... // Error - Ambiguous name from two modules!
As already documented this can be fixed with a single-type import which shadows both module imports for List:
import module java.base;
import module java.desktop;
import java.util.List;
import javax.swing.text.Element;
Element l = ... // javax.swing.text.Element
List l = ... // java.util.List
With the proposed change, a more general import statement can also fix this problem, and forestall any similar problems in the future, if they stem from the same packages:
import module java.base;
import module java.desktop;
import java.util.*;
import javax.swing.text.*;
Element l = ... // javax.swing.text.Element
Document d = ... // javax.swing.text.Document, regardless of any module imports
Position p = ... // javax.swing.text.Position, regardless of any module imports
List l = ... // java.util.List
Map m = … // java.util.Map, regardless of any module imports
Map m = … // java.util.Map, regardless of any module imports
I believe that real use cases (as opposed to simple motivating examples), it will be common for a user to work with names like Element, Document, List, Map, and all with reference to a few "foreground" packages in use. This is why, today, on-demand imports are so very popular; they make explicit, in a concise way, which are the "foreground" packages.
Now, in such a workflow with "foreground" packages, suppose a user wishes to "clean up" other miscellaneous imports by covering them all with one or more module imports. It's a risky and frustrating proposition, because all of the previously functioning on-demand imports (for the "foreground" packages) will begin to malfunction due to non-local effects from the module imports.
The team has already inspected java.base and found a fairly small number of name conflicts. However, if this feature is used with non-JDK class libraries which also contain conflicting uses of common names, then the module-import feature will prove disruptive, to the extend that it requires continual pointwise correction of ambiguities, even after the user has specified the "foreground" packages, as is often customary. To the extent this happens, the module-import feature will be something used on the "Java onramp" but will be discarded when the program scales up enough to maek conflict management into a new problem.
(IDEs could solve this problem by requesting single-type imports, as many shops do. But such IDE-produced import bundles are complex and shifting, and which will get more complex nd shifting as module imports are added. Not everybody likes single-type imports that well; some prefer the on-demand imports.)
Perhaps the worst-case scenario will be when there are two competing class libraries, which tend to define the same type names, come into conflict, and one or both of the conflicting packages are in module imports. Specifically, if I want java.base, but with some other package defining List/Set/Map, I still want to on-demand import that package, in a way that shadows java.util types. The present proposal preserves the use of any pre-existing on-demand import, for that "other package". Short of an IDE enumerating all single-type imports (and keeping it up as the user makes edits), the only way to reduce the import management burden is to delete one or both of the offending module-imports, and go back to the previous langauge features.
says this:
> A type-import-on-demand declaration never causes any other declaration to be shadowed.
And there are examples later on which enforce this, showing that only a single-type-import declaration is capable of resolving a name ambiguity set up by a single-module-import declaration.
This is sub-optimal, because it makes ineffective an on-demand import of multiple names. This is a very popular feature of the language, and if users are forced to choose between module-import-on-demand and package-import-on-demand, they not adopt the new feature. Why would they be forced to choose? Because the ambiguity effects of a broad module import would be resolvable, not at the granularity of packages, but rather via the fine-grained (and therefore laborious) enumeration of one import per ambiguous.
Perhaps this sub-optimaility occurs (in practice) rarely enough that we don't care. And yet the following simple change (replacing the line above) would remove the problem permanently.
> A type-import-on-demand declaration d in a compilation unit c of package p that imports a type named n (from p) shadows, throughout c, the declarations of any type named n imported by a single-module-import declaration in c.
This can be explained by saying that, just as a more specific single-type-import declaration shadows any conflicting type-import-on-demand declaration (because it is less specific), a type-import-on-demand declaration is in turn more specific than any conflicting single-module-import declaration.
The existing rules turn a single-module-import declaration into an effectual "macro" that expands into many type-import-on-demand declarations, but surely the existence of an explicit, user-written type-import-on-demand declaration (just like an explicit single-type-import declaration) should overshadow any implicitly "macro-expanded" declaration.
The examples with List and Element in the narrative further down can be adjusted to suggest either kind of non-module import as a way to fix the problem shown.
To continue with previous examples of ambiguity, suppose the program has module imports that produce an ambiguity on List and/or Element:
import module java.base;
import module java.desktop;
Element e = ... // Error - Ambiguous name from two packages in one module!
List l = ... // Error - Ambiguous name from two modules!
As already documented this can be fixed with a single-type import which shadows both module imports for List:
import module java.base;
import module java.desktop;
import java.util.List;
import javax.swing.text.Element;
Element l = ... // javax.swing.text.Element
List l = ... // java.util.List
With the proposed change, a more general import statement can also fix this problem, and forestall any similar problems in the future, if they stem from the same packages:
import module java.base;
import module java.desktop;
import java.util.*;
import javax.swing.text.*;
Element l = ... // javax.swing.text.Element
Document d = ... // javax.swing.text.Document, regardless of any module imports
Position p = ... // javax.swing.text.Position, regardless of any module imports
List l = ... // java.util.List
Map m = … // java.util.Map, regardless of any module imports
Map m = … // java.util.Map, regardless of any module imports
I believe that real use cases (as opposed to simple motivating examples), it will be common for a user to work with names like Element, Document, List, Map, and all with reference to a few "foreground" packages in use. This is why, today, on-demand imports are so very popular; they make explicit, in a concise way, which are the "foreground" packages.
Now, in such a workflow with "foreground" packages, suppose a user wishes to "clean up" other miscellaneous imports by covering them all with one or more module imports. It's a risky and frustrating proposition, because all of the previously functioning on-demand imports (for the "foreground" packages) will begin to malfunction due to non-local effects from the module imports.
The team has already inspected java.base and found a fairly small number of name conflicts. However, if this feature is used with non-JDK class libraries which also contain conflicting uses of common names, then the module-import feature will prove disruptive, to the extend that it requires continual pointwise correction of ambiguities, even after the user has specified the "foreground" packages, as is often customary. To the extent this happens, the module-import feature will be something used on the "Java onramp" but will be discarded when the program scales up enough to maek conflict management into a new problem.
(IDEs could solve this problem by requesting single-type imports, as many shops do. But such IDE-produced import bundles are complex and shifting, and which will get more complex nd shifting as module imports are added. Not everybody likes single-type imports that well; some prefer the on-demand imports.)
Perhaps the worst-case scenario will be when there are two competing class libraries, which tend to define the same type names, come into conflict, and one or both of the conflicting packages are in module imports. Specifically, if I want java.base, but with some other package defining List/Set/Map, I still want to on-demand import that package, in a way that shadows java.util types. The present proposal preserves the use of any pre-existing on-demand import, for that "other package". Short of an IDE enumerating all single-type imports (and keeping it up as the user makes edits), the only way to reduce the import management burden is to delete one or both of the offending module-imports, and go back to the previous langauge features.