Star or wildcard imports like import java.util.* are one of the features of Java that have not aged well. When Java 1.0 was released, 30 years ago, star imports seemed like a good idea. Anyone coming from C/C++ would be familiar with the concept of header files and the #include <file.h> directive, and how they could result in long lists of includes at the top of a file. Star imports were meant to be a convenience to avoid that boilerplate.

Since their introduction, star imports have been divisive. Some style guides recommend them, while others limit them or forbid them entirely. Some influential authors like Uncle Bob Martin recommended them (“Avoid long import lists by using wildcard”). Why the controversy?

Let’s first look at the advantages of star imports. There’s basically only one: they reduce the number of lines at the top of the Java file, and they avoid the need to add new imports when you use a new class from the same package. That’s it. Just convience for the programmer and a slightly neater look. The argument of convience used to be stronger in the 1990s when IDEs were not as amazing as they are today and programmers had to manually type every import line. However, this argument no longer holds water. Modern IDEs can automatically add imports as the programmer types (or as the AI writes code). Nobody has to manually type import lines anymore. Similarly, regarding the neater look, modern IDEs collapse the import list to avoid visual clutter.

I’m surprised that a language that came much later, like Kotlin, still has star imports, particularly since Kotlin was designed by a company that makes IDEs.

The arguments againts star imports are multiple. Let’s start with the weaker ones. Star imports hide the extent of the dependencies of a class, or in other words, they hide a visual measure of coupling. Of course there are tools to measure coupling more accurately, but a quick glance at the import list gives a good approximation, in particular when reading code outside of an IDE. This is notably the case of code reviews.

Another argument that is also showing its age is that enumerating the imports helps with code analysis and search. For example, it makes it possible to grep or git grep, or to do a simple text search (Ctrl-F/Cmd-F) to find usages of a class. Arguably IDEs have made this argument weaker, as long as the code is opened in an IDE.

Star imports have no impact on runtime performance, but they may have a small impact on compile time performance. In most cases, the impact is negligible.

The strongest argument against star imports is that they make the code more brittle. Consider a program that uses two libraries, A and B. The former provides Foo, and the latter provides Bar:

import com.example.a.*;
import com.example.b.*;

public class MyClass {
    private Foo foo; // comes from library A
    private Bar bar; // comes from library B
}

Note the library authors have followed the best practices and have used a inverted DNS name for their package names, ensuring that there are no name clashes between the two libraries.

That code compiles and works fine. But then, one day, library B adds a new class called Foo. From the point of view of the author of library B, adding a class is a perfectly valid backwards-compatible change which should not cause problems for anyone. However, our code no longer compiles, because the reference to Foo is now ambiguous. The compiler does not know which Foo we mean, the one from library A or the one from library B. The fix is easy, we just need to qualify the reference to Foo. However, the point is that the problem should not have happened in the first place. Doing a star import is not only importing the classes that we currently use, but also all the classes that may be added in the future, which is outside of our control.

In summary, star imports are a relic of the past and should be avoided. Their benefits are minimal, while their drawbacks are tangible. Just remember to configure your IDE to not use star imports when organizing imports.