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.

Syntaximport module java.net.http; import module jdk.httpserver; import java.sql.Date; // a single-type import wins over module imports

import 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 requires in module-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.ch stay hidden.
  • Fixing an ambiguity by removing the module import instead of adding one single-type import.

Example

Try it yourself
Loading editor…
Press Run to execute the code.

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.

Example · java
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
// 0

Module 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>.

Example · java
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 minutes

Transitive 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.

Example · java
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

  • Be the first to comment on this lesson.