Too many imports are spamming my Java code
Asked Answered
C

8

34

In my project I have a shapes package which has shapes I designed for my graphics program, e.g., Rectangle and Circle. I also have one or two more packages that have the same names as java.awt classes.

Now, since I do not want to rename every class in my codebase, to show my source files which class I mean when I, say, declare a new Rectangle, I need to either:

1- import the rectangle class explicitly, i.e., import shapes.Rectangle

or

2- import only the java.awt classes I need and not import java.awt.* which automatically includes the awt.Rectangle

Now the problem is that both ways result in a lot of importing. I currently have an average of 15-25 imports in each source file, which is seriously making my code mixed-up and confusing.

Is too many imports in your code a bad thing? Is there a way around this?

Chelicera answered 13/12, 2011 at 7:25 Comment(3)
Discussed in youtu.be/FyCYva9DhsI?t=1674, "Clean Coders Hate What Happens to Your Code When You Use These Enterprise Programming Tricks", great talk by Kevlin Henney ^^Musette
The talk is from February 2017, NDC London 2017. Channel "NDC Conferences". NDC = Norway Developer Conference (it started there).Spearmint
Also mentioned in an answer (27 min 54 secs).Spearmint
K
37

Yes, too many imports is a bad thing because it clutters your code and makes your imports less readable.

Avoid long import lists by using wildcards.

Kevlin Henney talks about this exact Stack Overflow question 27:54 into his presentation Clean Coders Hate What Happens to Your Code When You Use These Enterprise Programming Tricks from NDC London 16-20 Jan 2017

Kwangju answered 3/5, 2017 at 5:52 Comment(7)
According to #147954 this is bad because it clutters and potentially confuses your namespaces.Posse
It is also nightmare to deal with merge conflicts just in teh import section, due to collapse into * after hitting some treshold or expansion when using less imports.Bentham
Funny, I came from that video to here. He recommends using wildcards to reduce the information down to which packages you are using which would increase readability. He didn't mention it but that would be at the cost of compilation time. Lots of imports are a symptom of breaking the Single Responsibility Principle. There is clearly lots of coupling too. Simplifying your code will automatically reduce the number of imports and make your code more readable. In fact at the end of his lecture, he demonstrates exactly that.Twinscrew
Agree with David, using wildcards simply hides the fact that your class has too many dependencies to begin with. The solution to too many imports is to simply use less imports; which you will be able to do by refactoring your class into several smaller ones.Filagree
I think an important reason for not using wildcards is that they can cause errors whenever anyone creates a class. This includes updates to dependencies. So whenever you update any dependency, that update can break you program. Even when the update itself is non-breaking. It would usually be a compile error. If you are unlucky, it could also compile but cause runime errors or change the behavior. This problem does not exist if you don't use wildcards.Pitiful
I don't understand why this is the top answer. There is no mention of the downsides of wildcard imports. A lot of 'enterprise developers' (referring to Henney's talk) have learnt this the hard way. I used to use wildcard imports, but switched to explicit only in 2008, and all my colleagues since then have agreed that wildcard imports can cause problems. I've given a fuller answer below.Guanabana
In my opinion, having a lot of imports has hardly any effect on readability. It's not like they are scattered all over the code; instead, they are just a block to scroll over when you do not need any of the information found there. To really improve readability, it's IMO more promising to follow ZombieTfk's advice and break the class down into smaller pieces with less dependencies each.Fenugreek
P
7
  • If you use glob imports, it's possible to break your code with a namespace clash just by updating a dependency that introduces new types (typically not expected to be a breaking change). It could be a pain to fix in a large codebase that was liberal with their use of glob imports. That is the strongest reason I can think of for why it's a good idea to specify dependencies explicitly.
  • It's easier to read code which has each of the imports specified, because you can see where types are coming from without requiring IDE specific features and mouse hovering, or going through large pages of library documentation. Many people read a lot of code outside the IDE in code review, diffs, git history, etc.
Pavior answered 18/6, 2020 at 11:58 Comment(1)
I always use explicit imports for these reasons. The first issue is particularly horrible when it happens. I like to know that a piece of code once written cannot suddenly break because someone adds a new class to another package. I agree with Kevlin Henney on a lot of his ideas, but not about this. He may not have thought of the issue.Profane
F
6

Another alternative is to type the fully qualified class name as you need it. In my example, there are two Element objects, one created by my org.opensearch.Element and the other org.w3c.dom.Element.

To resolve the name conflict, as well as to minimize import "clutters", I've done this (in my org.opensearch.Element class):

public org.w3c.dom.Element toElement(org.w3c.dom.Document doc) { /* .... */ }

As you can see, the return Element type is fully-typed (i.e., I've specified the fully-qualified class name of Element).

Problem solved! :-)

Filibertofilibuster answered 13/12, 2011 at 7:31 Comment(0)
G
6

I use explicit imports, and have done so for many years. In all my projects in the last decade this has been agreed with team members, and all are happy to agree to to use explicit imports, and avoid wildcards. It has not been controversial.

In favour of explicit imports:

  • precise definition of what classes are used
  • less fragile as other parts of the codebase changes
  • easier to code review
  • no guessing about which class is in which package

In favour of wildcards:

  • less code
  • easier to add and maintain the imports when using a text editor

Early in my career, I did use wildcard imports, because back then IDEs were not so sophisticated, or some of us just used text editors. Managing explicit imports manually is quite a bit of effort, so wildcard imports really help.

However at least once I was bitten by the use of wildcard imports, and this lead be to my current policy of explicit only.

Imagine you have a class that has 10 wildcard imports, and it compiles successfully. Then you upgrade 5 jar files to newer versions (you have to upgrade them all, because they are related). Then the code no longer compiles, there is a class not found error. Now which package was that class in? What is the full name of the class? I don't know because I'm only using the short name, now I have to diff the old and new versions of the jars, to see what has changed.

If I had used explicit imports, it would be clear which class had been dropped, and what it's package was, and this which jar (by looking of other classed in that package) is responsible.

There are also problems reading code history, or looking at historic merges. When you have wildcard imports there is uncertainty for the reader about which class is which, and thus what the semantics of the code changes are. With explicit imports you have a precise description of the code, and it acts as a better historical record.

So overall the benefit of the small amount of extra effort to maintain the import, and extra lines of code are easily outweighed by extra precision and determinism given by explicit imports.

The only case when I still use wildcards, is when there are more that 50 imports from the same package. This is rare, and usually just for constants.


Update1: To address the comment of Peter Mortensen, below...

The only defence Kevlin Henney makes in his talk for using wildcards is that the name collision problem doesn't happen very often. But it's happened to me, and I've learnt from it. And I discuss that above.

He doesn't cover all the points I've made in my answer above. -- But most importanly, I think the choice you make, explicit or wildcard, doesn't matter that much, what matters is that everyone on the project/codebase agree and use a consistent style. Kevlin Henney goes on to talk about cargo-cult programming. My decisions as stated above are based on personal lessons over decades, not cargo cult reasoning.

If I was to join a project where the existing style was to use wildcards, I'd be fine with it. But if I was starting a new project I'd use precise imports.

Interestingly in nodejs there is no wildcard option. (Although you do have 'default' imports, but it's not quite the same).

Guanabana answered 22/12, 2020 at 15:10 Comment(2)
Kevlin Henney talks (see the other answer) about IDEs (as a bad excuse for not using wildcards, if I remember correctly). Perhaps address Kevlin Henney's point as well (incl. an exact time reference)?Spearmint
.. comment moved to answer above ...Guanabana
P
4

I don't get all the non-answers posted here.

Yes, individual imports are a bad idea, and add little, if anything, of value.

Instead just explicitly import the conflicts with the class you want to use (the compiler will tell you about conflicts between the awt and shapes package) like this:

import java.awt.*;
import shapes.*;
import shapes.Rectangle; // New Rectangle, and Rectangle.xxx will use shapes.Rectangle.

This has been done for years, since Java 1.2, with awt and util List classes. If you occasionally want to use java.awt.Rectangle, well, use the full class name, e.g., new java.awt.Rectangle(...);.

Perimeter answered 19/3, 2020 at 23:52 Comment(1)
Can you explain why some answers are "non-answers", and contrast them, and the drawbacks that they mention to your own answer? I think the conflict issue mentioned in some answers is more seriously about future conflicts that are introduced as a result of a dependency update, rather than conflicts at the time you are writing the code which are easy to identify and fix when they occur.Pavior
I
4

It's subjective and depends greatly on the circumstances. I sometimes bounce between the two.

It is a general good practice to be specific by default but it can also be higher maintenance. It's not a perfect rule. However being more specific (higher initial cost) will tend to reveal itself earlier through measurable or perceptible drag where as being lazy by default tends to manifest as a problem more adversely.

Over including of entire namespaces can create bloat and clashes as well as hide changes in structure but in certain cases it may outweigh the benefit.

To give a simple case:

I use a package with a hundred classes.

  • What if I use one of its classes?
  • What if I use all but one of them?

It's similar to the whitelist versus blacklist problem.

In an ideal situation the package hierarchy will subdivide package types enough to establish a good balance.

In certain systems I've seen people do the equivalent of import *. Perhaps the most horrifying case is when I saw someone do something similar to apt install *. Though it seemed clever so as to never have to worry about missing dependencies it imposed enormous burdens that far outweighed any benefit.

All things can be taken to the extreme. For example I could argue for the utility of imports as close to as needed but if we're going to do that why not just always use the fully qualified names all the time?

Problems like this are annoying as the low cognitive load of consistency is always preferable but when it comes down to it in various given circumstances you may need to play it by ear.

It's important to be proportionate. Doing something a thousand times to avoid something that happens one time, self presents and takes about as much effort to fix tends to result in a waste.

Different objectives may make "too many" imports a good thing. I have a custom JavaScript framework which would likely horrify many at a glance for its stacks of imports.

What's not immediately obvious is that this is part of a deliberate strategy.

  • This allows it to be easy In being able to more cleanly package the code with all its specific dependencies and then transmit that over a network.
  • Imports have a plug nature at build time to alternate dependencies for each given platform target.

This tends not to be as much as a problem for languages that are less dynamic and that do not suffer greatly (or at all) from the overhead excessively importing namespaces. The moral of this story is that import strategies can vary enormously but be valid for their given circumstances. You can only go so far in taking general approaches.

In each situation you will need to have your bearings and a sense of the lay of the land. If the import conventions and structure is causing a nuisance then it's necessary to narrow down the how and why. Too many imports may not be the result of a specific strategy but things such as packing too much into a single file. At the same time some files are naturally large and naturally require many imports.

Badly applied separation of concerns or organisation in failing to keep related things together can create a graph with grossly excessive edges where that can be reduced with greater organisation. To some degree it's not abnormal for code to be clustered by specific dependencies more so specific dependencies than the more general ones.

If a code base is well organised into a graph that is fairly close to optimal with neither excessive splitting, merging and minimal distance between things you will tend to find that even if being specific with imports the majority of cases will tend to stay within a reasonable size.

Isooctane answered 24/9, 2020 at 15:38 Comment(1)
This is a good answer, and a good discussion. Similarly in my answer I didn't say one approach is better, I just said what I do and why I do it. It's not the case that one approach is better than the other in all situations.Guanabana
B
1

It's normal in Java world to have a lot of imports - you really need to import everything. But if you use an IDE, such as Eclipse, it does the imports for you.

Bentham answered 13/12, 2011 at 7:30 Comment(0)
L
1
  • It's a good practice to import class by class instead of importing whole packages

  • Any good IDE, such as Eclipse, will collapse the imports in one line, and you can expand them when needed, so they won't clutter your view

  • In case of conflicts, you can always refer to fully qualified classes, but if one of the two classes is under your control, you can consider renaming it (with Eclipse, right click on the class, choose menu RefactorRename, it will take care to update all its references).

  • If your class is importing from AWT and from your package of shapes, is ok. It's ok to import from several classes; however, if you find yourself importing from really lots of disparate sources, it could be a sign that your class is doing too much, and need to be split up.

Lonnylonslesaunier answered 13/12, 2011 at 7:40 Comment(3)
This answer does not contain any arguments as to why it is better to use explicit imports over wildcards, but only states that 'IDEs make it less of a pain' i.e. treating the symptom, rather than the cause. This is why I decided to downvote this answer.Suannesuarez
For me, when I read my code over Github or in any text editor, I can exactly know which class is ThirdPartyClass resides in. Rather than recursively looking at those 6-7 * imported classes. Another instance, when I opened a legacy project and 2-3 of those libraries were not available. They had used * imports and it was difficult to know from which package that non-findable class was coming from. Only if they had used explicit names, it would have been quite easy from the package name.Tombola
Rather than using * imports just design your classes in such a way that you don't have too many imports. Any standard IDE will collapse those imports. Even when I look at the code over Github I can just skip to the class, so for me it doesn't clutter at all. Also, yes I have seen the talk but my answer remains the same. They are not always needed doesn't mean they are not needed.Tombola

© 2022 - 2024 — McMap. All rights reserved.