Module Import Declarations in Practice
Import every package a module exports with one line, resolve the name clashes it causes, and decide where module imports belong in real code.
import module java.net.http;
import module jdk.httpserver;
import java.sql.Date; // a single-type import wins over module importsimport module M; (JEP 511, final in Java 25) imports all public top-level types in every package that module M exports. It also includes the packages of modules that M requires transitively. Your own code does not have to be a module: it works from ordinary class-path code and from compact source files.
import module java.net.http; // java.net.http package
import module java.sql; // java.sql, javax.sql, plus java.xml and java.logging
Transitive modules come along
java.sql requires java.logging and java.xml transitively, so import module java.sql; also makes Logger and DocumentBuilderFactory available. That is convenient, but it is also how unexpected names end up in scope.
Name clashes and how to settle them
Two modules can export types with the same simple name. java.base has java.util.Date and java.sql has java.sql.Date, so this fails to compile with reference to Date is ambiguous:
import module java.base;
import module java.sql;
...
Date d = ...; // error
Module imports have the lowest priority. A single-type import such as import java.sql.Date; wins over both of them, and so does a package import such as import java.util.*;. Add one line for the type you mean and the clash disappears.
Compact source files
A compact source file already imports module java.base automatically. Add other module imports at the top as needed. This is why a quick script can use List, Files and Duration with no imports at all.
Where to use them
Module imports shine in scripts, teaching material, prototypes and JShell sessions, where import lists are noise. In a long-lived service, many teams keep explicit single-type imports, because they show exactly which types a class depends on and they cannot become ambiguous when a dependency grows. Treat import module like a package wildcard: fine where speed of writing matters more than precise dependencies.
Mistakes people make
- Assuming a module import is the same as
requiresinmodule-info.java. It only affects name lookup in one file; module resolution is unchanged. - Expecting non-exported packages to appear. Internal packages such as
sun.nio.chstay hidden. - Fixing an ambiguity by removing the module import instead of adding one single-type import.
Example
When to use it
- A diagnostic script that starts a local <code>HttpServer</code> stub and calls it with <code>HttpClient</code> using two import lines.
- A one-off migration script that reads XML exports and writes rows through the <code>java.sql</code> API.
- Teaching material and internal runbooks where long import lists distract from the code that matters.
More examples
Settling an ambiguous name
Without <code>import java.sql.Date;</code> this file does not compile, because the compact file's automatic <code>java.base</code> import and <code>java.sql</code> both offer a <code>Date</code>. One explicit import is the whole fix.
import module java.sql;
import java.sql.Date;
void main() {
// java.util.Date (from java.base) and java.sql.Date both match "Date";
// the single-type import above decides it.
Date due = Date.valueOf("2026-03-01");
IO.println(due + " is a " + due.getClass().getName());
// java.util.Date is still usable by its full name.
java.util.Date epoch = new java.util.Date(0L);
IO.println(epoch.getTime());
}
// Output:
// 2026-03-01 is a java.sql.Date
// 0Module imports in an ordinary class
<code>import module java.base;</code> covers <code>java.util</code> and <code>java.time</code> in one line in a normal named class. The class itself still sits on the class path; it does not need a <code>module-info.java</code>.
import module java.base;
public class Report {
public static void main(String[] args) {
Map<String, Integer> byStatus = new TreeMap<>();
for (String s : List.of("PAID", "OPEN", "PAID", "REFUNDED")) {
byStatus.merge(s, 1, Integer::sum);
}
Duration window = Duration.ofMinutes(90);
IO.println(byStatus + " in the last " + window.toMinutes() + " minutes");
}
}
// Output:
// {OPEN=1, PAID=2, REFUNDED=1} in the last 90 minutesTransitive modules: XML through java.sql
<code>DocumentBuilderFactory</code>, <code>Document</code> and <code>InputSource</code> come from <code>java.xml</code>, reached through <code>java.sql</code>'s transitive dependency. Handy in a script, but it shows why explicit imports are clearer in production code.
import module java.sql;
void main() throws Exception {
// java.sql requires java.xml transitively, so its types are imported too.
String xml = "<config><pool size=\"10\"/><pool size=\"4\"/></config>";
DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document doc = builder.parse(new InputSource(new StringReader(xml)));
NodeList pools = doc.getElementsByTagName("pool");
int total = 0;
for (int i = 0; i < pools.getLength(); i++) {
Element pool = (Element) pools.item(i);
total += Integer.parseInt(pool.getAttribute("size"));
}
IO.println("pools " + pools.getLength() + ", connections " + total);
}
// Output:
// pools 2, connections 14
Discussion