Skip to content
Ameen Abozaid
PublishedBEG-027-L02

Imports and name clashes

  • Lesson 2 of 5
  • 11 min read
  • Beginner
On This Lesson 11 topics

Lesson Overview

In the last lesson you put classes into packages. A class in one package now has a longer name, such as com.example.bank.Account. Writing that full name every time you use the class would be tiring and hard to read. The import statement is the shortcut: it tells the compiler "when I write Account, I mean com.example.bank.Account".

In our filing cabinet picture, an import is a note on your desk that says which drawer a document comes from, so you can refer to it by its short title. The document is not copied to your desk, and nothing is loaded. You just get a shorter way to name it.

Why this matters: every Java file you will read or write in a backend project begins with a block of imports. You need to read that block quickly, avoid the * shortcut in serious code, and know what to do when two libraries both offer a class with the same name. That last situation is common: dates, lists and loggers all appear in several libraries.

This lesson builds on "Packages and folders" and uses the same com.example layout.

Learning Objectives

By the end of this lesson you will be able to:

  • Explain what an import does and what it does not do.
  • Write single-type imports in the correct place in a file.
  • Say which classes need no import (same package, java.lang).
  • Explain what import java.util.*; does and where it stops.
  • Resolve a name clash between two classes by importing one and spelling out the other.
  • Read the compiler errors for a missing import and for an ambiguous name.
  • Explain why explicit imports are preferred in real code.

Written Explanation

What an import is

An import is a naming shortcut, nothing more. The Java Language Specification describes imports as letting code use simple names for types from other packages, where otherwise it would need fully qualified names. An import does not copy code, does not run anything and does not decide what is compiled. If you wrote java.util.List everywhere instead, the program would behave identically.

Imports go after the package line and before the class:

java
package com.example.app;

import com.example.bank.Account;
import java.util.List;

public class Main { }

Each import line names one class by its full name and ends with a semicolon. This form is called a single-type import.

Three cases where you need no import

  1. The class is in the same package. All classes of one package see each other by simple name. In our test, Printer and Statement both live in com.example.bank, so Printer uses Statement with no import.
  2. The class is in java.lang. The JLS says every file behaves as if it began with import java.lang.*;. That is why String, Object, Math and System work with no import.
  3. You write the fully qualified name. java.util.List<String> names = ... needs no import line, because there is nothing to shorten.

Everything else needs an import (or a full name): List, ArrayList, Scanner, and every class of your own that lives in a different package.

The wildcard import

The second form is the on-demand import, written with *:

java
import java.util.*;

It makes all the accessible classes directly in java.util available by simple name. Points to know:

  • It does not import sub-packages. import java.util.*; gives you List, but not anything from java.util.concurrent. Hierarchy in names has no special meaning to Java: the JLS says there is no access relationship between oliver.twist and oliver.
  • It does not make the program bigger or slower. It is only a name lookup rule for the compiler.
  • It can create ambiguity, as the next section shows.

Most teams prefer explicit single-type imports in real code: a reader sees exactly which classes a file depends on, and a new class added to a wildcard-imported package cannot silently clash with yours. Your IDE can add and tidy imports for you (the menu names vary by IDE, so check its help for "organize imports" or similar).

Name clashes

Two different packages may contain classes with the same simple name. The standard library has java.util.Date and java.sql.Date. If both are in play in one file, Date alone is ambiguous. There are three situations.

Situation 1: two wildcard imports.

java
import java.util.*;
import java.sql.*;
// ... Date d = new Date(0L);

The compiler refuses at the point of use:

text
error: reference to Date is ambiguous
  both class java.sql.Date in java.sql and class java.util.Date in java.util match

Situation 2: two single-type imports of the same simple name.

java
import java.util.Date;
import java.sql.Date;
text
error: a type with the same simple name is already defined by the single-type-import of Date

The JLS says two single-type imports of the same simple name from different types are an error.

Situation 3: the fix. Import one class and write the other with its full name:

java
import java.util.Date;

java.sql.Date day = new java.sql.Date(0L);

Choose the class you use most as the imported one. The full name is always legal, wherever the class is accessible.

A single-type import also wins over a wildcard import of the same simple name, so import java.util.Date; together with import java.sql.*; resolves Date to java.util.Date. Do not lean on that. Explicit is easier to read.

Imports and visibility are separate

An import never grants access. A class you import must also be accessible. A class that is not public can only be used inside its own package, even with an import line. The next lesson is about that rule.

Two limits to remember

  • Each file has its own imports. They do not carry over to other files, even in the same package.
  • You cannot import a package alone. import java.util; is an error. You import a class (java.util.List) or all classes (java.util.*).

Examples

Example 1: a missing import (simple)

Main uses Account from another package but has no import line:

java
package com.example.app;

public class Main {
    public static void main(String[] args) {
        Account a = new Account("Sara", 100);
    }
}
text
error: cannot find symbol
        Account a = new Account("Sara", 100);
        ^
  symbol:   class Account
  location: class Main

The compiler reports "cannot find symbol" for the type and again for the constructor call. The fix is one line after the package line: import com.example.bank.Account;. Whenever you see cannot find symbol ... class X for a class you know exists, ask: is it in a different package, and did I import it?

Example 2: classes that need no import (practical)

Two classes in package com.example.bank:

java
package com.example.bank;

public class Statement {
    public String title() { return "Monthly statement"; }
}
java
package com.example.bank;

public class Printer {
    public static String print(Statement s) {   // same package: no import
        return "[" + s.title() + "]";
    }
}

Main in package com.example.app needs both:

java
package com.example.app;

import com.example.bank.Printer;
import com.example.bank.Statement;

public class Main {
    public static void main(String[] args) {
        System.out.println(Printer.print(new Statement()));
        String s = "lang";            // String is in java.lang: no import
        System.out.println(s.length());
    }
}

Output:

text
[Monthly statement]
4

Printer needed no import for Statement; Main needed two imports; String needed none.

Example 3: using both Date classes (realistic)

A reporting class reads timestamps from an application and writes database dates. It needs both Date types:

java
package com.example.app;

import java.util.Date;
import java.util.List;

public class Clash {
    public static void main(String[] args) {
        Date now = new Date(0L);
        java.sql.Date day = new java.sql.Date(0L);
        List<String> names = List.of("a", "b");
        System.out.println(now.getTime() + " " + day.getTime() + " " + names.size());
        System.out.println(day.getClass().getName());
    }
}

Output (JDK 21):

text
0 0 2
java.sql.Date

Date means java.util.Date because that is the one imported. The other is written out in full each time it is used. If you used java.sql.Date many times, you might import that one and spell out the other instead. Pick whichever makes the file easier to read. Some teams avoid the clash by wrapping one of the two in their own class so the rest of the code never sees both.

Backend Insight

Backend code constantly mixes classes from several libraries: your own domain classes, JDK classes, logging, database and web libraries. Name clashes happen in practice with Date, List, Logger and many Request/Response types. The reversed-domain package convention from Lesson 1 is exactly what makes the clash solvable: the full names are different even when the simple names match.

Reading imports is also a fast way to understand a file. The import block tells you what a class depends on: a bank.account class importing from bank.notification shows a link between two features. When imports from many unrelated packages pile up in one class, that is a signal the class does too much. In Lesson 4 we use these import lines to see how packages depend on each other.

Common Mistakes

Mistake 1: thinking an import loads or copies code

An import line changes how names are written, not what the program does. A program works the same with all imports replaced by full names.

Mistake 2: forgetting that the wildcard does not include sub-packages

import java.util.*; does not import java.util.concurrent.ExecutorService, because that class lives in a sub-package. The symptom is the usual cannot find symbol. Import what you need explicitly.

Mistake 3: wildcard imports from two packages that share a name

import java.util.*; import java.sql.*; is fine until you write Date. Then the compiler reports an ambiguity. Switch to single-type imports.

Mistake 4: trying to import a class from the default package

text
import Helper;
error: '.' expected

An import needs a package name. This is one more reason to avoid the unnamed package.

Mistake 5: assuming an import makes a hidden class usable

If a class is not public, an import line does not help from another package. The compiler says "is not public ... cannot be accessed from outside package". See the next lesson.

Exercises

Try each exercise yourself before opening the solutions.

Level A: Foundation

A1. Which of these need an import in a class of package com.example.app: String, ArrayList, Math, a class com.example.bank.Account?

Show solution A1

A1. ArrayList and Account need imports. String and Math are in java.lang.

A2. Write the import line for java.util.Scanner, and say where it goes relative to the package line.

Show solution A2

A2. import java.util.Scanner;. It goes after the package line (if any) and before the class.

A3. True or false: import java.util.*; also imports java.util.concurrent.TimeUnit. Explain briefly.

Show solution A3

A3. False. The wildcard imports only the classes directly in java.util, not those of sub-packages, so TimeUnit needs import java.util.concurrent.TimeUnit; or import java.util.concurrent.*;.

Level B: Application

B1. This code does not compile. Name the error type and fix it:

java
package com.example.app;

public class Report {
    public static void main(String[] args) {
        List<String> lines = new ArrayList<>();
        lines.add("x");
    }
}
Show solution B1

B1. cannot find symbol for List and ArrayList. Add import java.util.List; and import java.util.ArrayList; after the package line.

B2. A file has import java.util.*; and import java.awt.*;. It uses List. Both packages have a class named List. What goes wrong, and how do you fix it, if you want java.util.List?

Show solution B2

B2. Using List is ambiguous because two on-demand imports both provide it, and the compiler reports an ambiguity error. Fix: add import java.util.List;, or replace the wildcards with single-type imports. Writing java.util.List in full also works.

B3. Rewrite this snippet so it compiles, using only one import line and no wildcard:

java
java.util.Date a = new java.util.Date(0L);
java.sql.Date b = new java.sql.Date(0L);
Show solution B3

B3.

java
import java.util.Date;

Date a = new Date(0L);
java.sql.Date b = new java.sql.Date(0L);

Importing the java.sql one and spelling out java.util.Date is equally correct.

Level C: Challenge

C1. Class Auditor in package com.example.bank calls Printer.print(...), where Printer is in package com.example.bank too. A teammate adds import com.example.bank.Printer; anyway. Is that an error? What would you say in code review?

Show solution C1

C1. It is not an error (the JLS lets code refer to the current package's types freely, and importing a type from your own package is harmless), but it is redundant noise. In review, ask to remove it, because the same-package rule makes the line unnecessary. Many IDEs flag it as unused or redundant.

C2. Explain why a team might forbid wildcard imports. Give two reasons.

Show solution C2

C2. Reasons: readers cannot tell which classes a file depends on; and a wildcard may start causing ambiguity when a new class with a clashing name is added to one of the packages. Also, tooling output about dependencies is clearer with explicit lines.

Mini Challenge

Take the project from Lesson 1 (com.example.shop.Product and com.example.app.Main). Add a second class PriceFormatter in com.example.shop that has a public static method format(Product p) returning text such as Pen costs 2.5, and a class Invoice in com.example.app that uses both Product and PriceFormatter and a java.util.List<Product>.

Check your work: Invoice has exactly three imports (two of your own, one java.util.List), uses no wildcard, and compiles. Now remove the import of Product and read the error. Then add a use of java.sql.Date and java.util.Date in the same file, importing only one.

Summary & Next Lesson

Key takeaways

  • An import is a naming shortcut for the compiler. It copies nothing and runs nothing.
  • Imports sit after the package line and before the class.
  • No import is needed for classes in the same package or in java.lang.
  • import x.y.*; imports the classes directly in x.y, not sub-packages, and can create ambiguity. Prefer explicit single-type imports.
  • When two classes share a simple name, import one and write the other with its full name.
  • An import never changes visibility: the imported class must still be accessible.

Next lesson

You have just seen the error "is not public ... cannot be accessed from outside package". The next lesson, "Package-private: the forgotten modifier", explains the visibility rule behind it and shows how to use it on purpose to hide helper classes.

Self-check

  1. What does import com.example.bank.Account; let you do?
  2. Why does String need no import?
  3. What does import java.util.*; leave out?
  4. How do you use both java.util.Date and java.sql.Date in one file?
  5. Does an import make a non-public class from another package usable?
Show answers
  1. Write Account instead of com.example.bank.Account in that file.
  2. It is in java.lang, which every file imports automatically.
  3. Classes in sub-packages such as java.util.concurrent.
  4. Import one of them and write the other with its full name.
  5. No. Visibility rules still apply, so the class must be public (or in the same package).

References

  • Oracle, The Java Language Specification, SE 21, Chapter 7: Packages and Modules. Supports the definition of single-type and on-demand imports, the automatic java.lang import, errors for duplicate single-type imports, that sub-packages are not imported, and that package hierarchy gives no special access.
  • Oracle, The Java Tutorials: Naming a Package. Supports the point that classes with the same simple name can live in different packages and are told apart by full names.
  • The compiler errors and program outputs were produced with JDK 21 in a scratch directory. The rule that a single-type import wins over a wildcard import comes from the JLS shadowing discussion as we read it; the on-demand/ambiguity compiler message was observed directly.

Practice

Apply this lesson with the module's lab, exercises and quiz.

Lab and exercises Quiz

Marking a lesson complete is optional and you can undo it any time. Your progress stays in this browser.

Continue to next lesson

Type to search levels, phases, modules and lessons.