简体   繁体   English

为什么在 Java 导入语句中使用通配符不好?

[英]Why is using a wild card with a Java import statement bad?

It is much more convenient and cleaner to use a single statement like使用像这样的单个语句更方便、更简洁

import java.awt.*;

than to import a bunch of individual classes而不是导入一堆单独的类

import java.awt.Panel;
import java.awt.Graphics;
import java.awt.Canvas;
...

What is wrong with using a wildcard in the import statement?import语句中使用通配符有什么问题?

The only problem with it is that it clutters your local namespace.唯一的问题是它使您的本地命名空间变得混乱。 For example, let's say that you're writing a Swing app, and so need java.awt.Event , and are also interfacing with the company's calendaring system, which has com.mycompany.calendar.Event .例如,假设您正在编写一个 Swing 应用程序,因此需要java.awt.Event ,并且还与公司的日历系统交互,该系统具有com.mycompany.calendar.Event If you import both using the wildcard method, one of these three things happens:如果您使用通配符方法导入两者,则会发生以下三种情况之一:

  1. You have an outright naming conflict between java.awt.Event and com.mycompany.calendar.Event , and so you can't even compile.您在java.awt.Eventcom.mycompany.calendar.Event之间存在彻底的命名冲突,因此您甚至无法编译。
  2. You actually manage only to import one (only one of your two imports does .* ), but it's the wrong one, and you struggle to figure out why your code is claiming the type is wrong.您实际上只导入了一个(两个导入中只有一个.* ),但它是错误的,并且您很难弄清楚为什么您的代码声称类型错误。
  3. When you compile your code there is no com.mycompany.calendar.Event , but when they later add one your previously valid code suddenly stops compiling.当您编译代码时,没有com.mycompany.calendar.Event ,但是当他们后来添加一个时,您以前有效的代码突然停止编译。

The advantage of explicitly listing all imports is that I can tell at a glance which class you meant to use, which simply makes reading the code that much easier.显式列出所有导入的好处是我可以一眼看出您打算使用哪个类,这使得阅读代码变得更加容易。 If you're just doing a quick one-off thing, there's nothing explicitly wrong , but future maintainers will thank you for your clarity otherwise.如果你只是做一件快速的一次性事情,没有什么明显的错误,但未来的维护者会感谢你的清晰说明。

Here's a vote for star imports.这是明星进口的投票。 An import statement is intended to import a package , not a class.导入语句旨在导入,而不是类。 It is much cleaner to import entire packages;导入整个包要干净得多; the issues identified here (eg java.sql.Date vs java.util.Date ) are easily remedied by other means, not really addressed by specific imports and certainly do not justify insanely pedantic imports on all classes.此处确定的问题(例如java.sql.Datejava.util.Date )很容易通过其他方式解决,具体导入并未真正解决,当然也不能证明对所有类进行疯狂迂腐的导入。 There is nothing more disconcerting than opening a source file and having to page through 100 import statements.没有什么比打开一个源文件并不得不翻阅 100 个导入语句更令人不安的了。

Doing specific imports makes refactoring more difficult;进行特定的导入会使重构更加困难; if you remove/rename a class, you need to remove all of its specific imports.如果您删除/重命名一个类,则需要删除其所有特定导入。 If you switch an implementation to a different class in the same package, you have to go fix the imports.如果将实现切换到同一个包中的不同类,则必须修复导入。 While these extra steps can be automated, they are really productivity hits for no real gain.虽然这些额外的步骤可以自动化,但它们确实是对生产力的打击,并没有真正的收益。

If Eclipse didn't do specific class imports by default, everyone would still be doing star imports.如果 Eclipse 默认不进行特定的类导入,那么每个人仍然会进行星型导入。 I'm sorry, but there's really no rational justification for doing specific imports.很抱歉,但确实没有合理的理由进行特定的进口。

Here's how to deal with class conflicts:以下是处理类冲突的方法:

import java.sql.*;
import java.util.*;
import java.sql.Date;

Please see my article Import on Demand is Evil请参阅我的文章按需导入是邪恶的

In short, the biggest problem is that your code can break when a class is added to a package you import.简而言之,最大的问题是当将类添加到您导入的包中时,您的代码可能会中断。 For example:例如:

import java.awt.*;
import java.util.*;

// ...

List list;

In Java 1.1, this was fine;在 Java 1.1 中,这很好; List was found in java.awt and there was no conflict.在 java.awt 中找到 List 并且没有冲突。

Now suppose you check in your perfectly working code, and a year later someone else brings it out to edit it, and is using Java 1.2.现在假设您签入了完美运行的代码,一年后有人将它拿出来进行编辑,并且正在使用 Java 1.2。

Java 1.2 added an interface named List to java.util. Java 1.2 向 java.util 添加了一个名为 List 的接口。 BOOM!繁荣! Conflict.冲突。 The perfectly working code no longer works.完美运行的代码不再有效。

This is an EVIL language feature.这是EVIL语言功能。 There is NO reason that code should stop compiling just because a type is added to a package...没有理由仅仅因为将类型添加到包中而停止编译代码......

In addition, it makes it difficult for a reader to determine which "Foo" you're using.此外,读者很难确定您使用的是哪个“Foo”。

It's not bad to use a wild card with a Java import statement.在 Java 导入语句中使用通配符也不错

In Clean Code , Robert C. Martin actually recommends using them to avoid long import lists.Clean Code中,Robert C. Martin 实际上建议使用它们来避免长导入列表。

Here is the recommendation:这是建议:

J1: Avoid Long Import Lists by Using Wildcards J1:使用通配符避免长导入列表

If you use two or more classes from a package, then import the whole package with如果您使用包中的两个或多个类,则使用以下命令导入整个包

import package.*;导入包。*;

Long lists of imports are daunting to the reader.长长的进口清单令读者望而生畏。 We don't want to clutter up the tops of our modules with 80 lines of imports.我们不想用 80 行导入来弄乱我们模块的顶部。 Rather we want the imports to be a concise statement about which packages we collaborate with.相反,我们希望导入是关于我们与哪些包合作的简明陈述。

Specific imports are hard dependencies, whereas wildcard imports are not.特定的导入是硬依赖,而通配符导入不是。 If you specifically import a class, then that class must exist.如果您专门导入一个类,则该类必须存在。 But if you import a package with a wildcard, no particular classes need to exist.但是,如果您使用通配符导入包,则不需要存在特定的类。 The import statement simply adds the package to the search path when hunting for names. import 语句只是在搜索名称时将包添加到搜索路径中。 So no true dependency is created by such imports, and they therefore serve to keep our modules less coupled.因此,此类导入不会创建真正的依赖关系,因此它们有助于减少我们的模块耦合。

There are times when the long list of specific imports can be useful.有时,一长串特定的进口产品可能会很有用。 For example, if you are dealing with legacy code and you want to find out what classes you need to build mocks and stubs for, you can walk down the list of specific imports to find out the true qualified names of all those classes and then put the appropriate stubs in place.例如,如果您正在处理遗留代码并且想要找出需要为哪些类构建模拟和存根,则可以遍历特定导入列表以找出所有这些类的真实限定名,然后输入适当的存根到位。 However, this use for specific imports is very rare.但是,这种用于特定进口的用途非常罕见。 Furthermore, most modern IDEs will allow you to convert the wildcarded imports to a list of specific imports with a single command.此外,大多数现代 IDE 将允许您使用单个命令将通配符导入转换为特定导入列表。 So even in the legacy case it's better to import wildcards.因此,即使在遗留情况下,最好导入通配符。

Wildcard imports can sometimes cause name conflicts and ambiguities.通配符导入有时会导致名称冲突和歧义。 Two classes with the same name, but in different packages, will need to be specifically imported, or at least specifically qualified when used.两个具有相同名称但在不同包中的类将需要专门导入,或者在使用时至少需要专门限定。 This can be a nuisance but is rare enough that using wildcard imports is still generally better than specific imports.这可能很麻烦,但很少见,使用通配符导入通常仍然比特定导入更好。

Performance : No impact on performance as byte code is same.性能:对性能没有影响,因为字节码是相同的。 though it will lead to some compile overheads.虽然它会导致一些编译开销。

Compilation : on my personal machine, Compiling a blank class without importing anything takes 100 ms but same class when import java.* takes 170 ms.编译:在我的个人机器上,编译一个空白类而不导入任何东西需要 100 毫秒,但导入 java.* 时相同的类需要 170 毫秒。

It clutters your namespace, requiring you to fully specify any classnames that are ambiguous.它使您的命名空间变得混乱,要求您完全指定任何不明确的类名。 The most common occurence of this is with:最常见的情况是:

import java.util.*;
import java.awt.*;

...
List blah; // Ambiguous, needs to be qualified.

It also helps make your dependencies concrete, as all of your dependencies are listed at the top of the file.它还有助于使您的依赖项具体化,因为您的所有依赖项都列在文件的顶部。

  1. It helps to identify classname conflicts: two classes in different packages that have the same name.它有助于识别类名冲突:不同包中具有相同名称的两个类。 This can be masked with the * import.这可以用 * 导入来掩盖。
  2. It makes dependencies explicit, so that anyone who has to read your code later knows what you meant to import and what you didn't mean to import.它使依赖项变得明确,以便以后必须阅读您的代码的任何人都知道您要导入什么以及您不打算导入什么。
  3. It can make some compilation faster because the compiler doesn't have to search the whole package to identify depdencies, though this is usually not a huge deal with modern compilers.它可以使一些编译更快,因为编译器不必搜索整个包来识别依赖关系,尽管这对于现代编译器来说通常不是什么大问题。
  4. The inconvenient aspects of explicit imports are minimized with modern IDEs.现代 IDE 将显式导入的不便之处降至最低。 Most IDEs allow you to collapse the import section so it's not in the way, automatically populate imports when needed, and automatically identify unused imports to help clean them up.大多数 IDE 允许您折叠导入部分,使其不碍事,在需要时自动填充导入,并自动识别未使用的导入以帮助清理它们。

Most places I've worked that use any significant amount of Java make explicit imports part of the coding standard.我工作过的大多数使用大量 Java 的地方都将显式导入作为编码标准的一部分。 I sometimes still use * for quick prototyping and then expand the import lists (some IDEs will do this for you as well) when productizing the code.我有时仍然使用 * 进行快速原型设计,然后在产品化代码时扩展导入列表(某些 IDE 也会为您执行此操作)。

I prefer specific imports, because it allows me to see all the external references used in the file without looking at the whole file.我更喜欢特定的导入,因为它允许我查看文件中使用的所有外部引用,而无需查看整个文件。 (Yes, I know it won't necessarily show fully qualified references. But I avoid them whenever possible.) (是的,我知道它不一定会显示完全限定的引用。但我尽可能避免使用它们。)

In a previous project I found that changing from *-imports to specific imports reduced compilation time by half (from about 10 minutes to about 5 minutes).在之前的项目中,我发现从 *-imports 更改为特定的导入将编译时间减少了一半(从大约 10 分钟到大约 5 分钟)。 The *-import makes the compiler search each of the packages listed for a class matching the one you used. *-import 使编译器在列出的每个包中搜索与您使用的类匹配的类。 While this time can be small, it adds up for large projects.虽然这个时间可能很小,但它对于大型项目来说是加起来的。

A side affect of the *-import was that developers would copy and paste common import lines rather than think about what they needed. *-import 的一个副作用是开发人员会复制和粘贴常见的导入行,而不是考虑他们需要什么。

In DDD bookDDD 书中

In whatever development technology the implementation will be based on, look for ways of minimizing the work of refactoring MODULES .在实现将基于的任何开发技术中,寻找最小化重构 MODULES 工作的方法。 In Java, there is no escape from importing into individual classes, but you can at least import entire packages at a time, reflecting the intention that packages are highly cohesive units while simultaneously reducing the effort of changing package names.在 Java 中,无法避免导入单个类,但您至少可以一次导入整个包,这反映了包是高度内聚的单元的意图,同时减少了更改包名称的工作量。

And if it clutters local namespace its not your fault - blame the size of the package.如果它使本地命名空间混乱,这不是你的错 - 归咎于包的大小。

Here are the few things that I found regarding this topic.以下是我发现的关于这个主题的几件事。

  • During compilation, the compiler tries to find classes that are used in the code from the .* import and the corresponding byte code will be generated by selecting the used classes from .* import.在编译过程中,编译器会尝试从.*导入中查找代码中使用的类,并通过从.*导入中选择使用的类来生成相应的字节码。 So the byte code of using .* import or .class names import will be same and the runtime performance will also be the same because of the same byte code.所以使用.* import 或 .class names import 的字节码是相同的,因为相同的字节码,运行时性能也会相同。

  • In each compilation, the compiler has to scan all the classes of .* package to match the classes that are actually used in the code.在每次编译中,编译器必须扫描.*包的所有类,以匹配代码中实际使用的类。 So, code with .* import takes more time during the compilation process as compared to using .class name imports.因此,与使用 .class 名称导入相比,使用.*导入的代码在编译过程中需要更多时间。

  • Using .* import helps to make code more cleaner使用.* import 有助于使代码更简洁

  • Using .* import can create ambiguity when we use two classes of the same name from two different packages.当我们使用来自两个不同包的两个同名类时,使用.* import 会产生歧义。 Eg, Date is available in both packages.例如,日期在两个包中都可用。

     import java.util.*; import java.sql.*; public class DateDemo { private Date utilDate; private Date sqlDate; }

The most important one is that importing java.awt.* can make your program incompatible with a future Java version:最重要的是导入java.awt.*会使你的程序与未来的 Java 版本不兼容:

Suppose that you have a class named "ABC", you're using JDK 8 and you import java.util.* .假设您有一个名为“ABC”的类,您使用的是 JDK 8,并且您导入了java.util.* Now, suppose that Java 9 comes out, and it has a new class in package java.util that by coincidence also happens to be called "ABC".现在,假设 Java 9 发布了,它在包java.util有一个新类,巧合的是,它也被称为“ABC”。 Your program now will not compile on Java 9, because the compiler doesn't know if with the name "ABC" you mean your own class or the new class in java.awt .您的程序现在无法在 Java 9 上编译,因为编译器不知道名称“ABC”是指您自己的类还是java.awt中的新类。

You won't have that problem when you import only those classes explicitly from java.awt that you actually use.当您仅从您实际使用的java.awt显式导入那些类时,您不会遇到这个问题。

Resources:资源:

Java Imports Java 导入

Among all the valid points made on both sides I haven't found my main reason to avoid the wildcard: I like to be able to read the code and know directly what every class is, or if it's definition isn't in the language or the file, where to find it.在双方提出的所有有效观点中,我还没有找到避免使用通配符的主要原因:我喜欢能够阅读代码并直接知道每个类是什么,或者它的定义是否不在语言中或文件,在哪里可以找到它。 If more than one package is imported with * I have to go search every one of them to find a class I don't recognize.如果使用 * 导入了多个包,我必须搜索每个包以找到我不认识的类。 Readability is supreme, and I agree code should not require an IDE for reading it.可读性是至高无上的,我同意代码不应该需要IDE 来阅读它。

  • There is no runtime impact, as compiler automatically replaces the * with concrete class names.没有运行时影响,因为编译器会自动将 * 替换为具体的类名。 If you decompile the .class file, you would never see import ...* .如果你反编译 .class 文件,你永远不会看到import ...*

  • C# always uses * (implicitly) as you can only using package name. C#始终使用 *(隐式),因为您只能using包名称。 You can never specify the class name at all.您根本无法指定类名。 Java introduces the feature after c#. Java 引入了 c# 之后的特性。 (Java is so tricky in many aspects but it's beyond this topic). (Java 在许多方面都非常棘手,但它超出了这个主题)。

  • In Intellij Idea when you do "organize imports", it automatically replaces multiple imports of the same package with *.在 Intellij Idea 中,当您“组织导入”时,它会自动将同一包的多个导入替换为 *. This is a mandantory feature as you can not turn it off (though you can increase the threshold).这是一项强制性功能,因为您无法将其关闭(尽管您可以提高阈值)。

  • The case listed by the accepted reply is not valid.接受的答复列出的案例无效。 Without * you still got the same issue.没有 * 你仍然有同样的问题。 You need specify the pakcage name in your code no matter you use * or not.无论您是否使用 *,您都需要在代码中指定 pakcage 名称。

For the record: When you add an import, you are also indicating your dependencies.作为记录:当你添加一个导入时,你也表明了你的依赖关系。

You could see quickly what are the dependencies of files (excluding classes of the same namespace).您可以快速看到文件的依赖关系(不包括相同命名空间的类)。

Forget about cluttered namespaces... And consider the poor soul who has to read and understand your code on GitHub, in vi, Notepad++, or some other non-IDE text editor.忘掉杂乱的命名空间……想想可怜的灵魂,他们必须在 GitHub、vi、Notepad++ 或其他一些非 IDE 文本编辑器中阅读和理解你的代码。

That person has to painstakingly look up every token that comes from one of the wildcards against all the classes and references in each wildcarded scope... just to figure out what in the heck is going on.该人必须煞费苦心地查找来自通配符之一的每个标记与每个通配符范围内的所有类和引用......只是为了弄清楚到底发生了什么。

If you're writing code for the compiler only - and you know what you're doing - I'm sure there's no problem with wildcards.如果你只为编译器编写代码——而且你知道自己在做什么——我相信通配符没有问题。

But if other people - including future you - want to quickly make sense of a particular code file on one reading, then explicit references help a lot.但是,如果其他人——包括未来的你——想在一次阅读中快速理解特定的代码文件,那么显式引用会有很大帮助。

Using wild card imports is not bad because Java tutorials by Oracle use wild card imports.使用通配符导入也不错,因为 Oracle 的 Java 教程使用通配符导入。 I don't think that Java folks at Oracle would do wrong thing.我认为 Oracle 的 Java 人员不会做错事。 Please look here: https://docs.oracle.com/javase/tutorial/uiswing/examples/components/CustomComboBoxDemoProject/src/components/CustomComboBoxDemo.java and here: https://docs.oracle.com/javase/tutorial/uiswing/examples/components/ .请看这里: https : //docs.oracle.com/javase/tutorial/uiswing/examples/components/CustomComboBoxDemoProject/src/components/CustomComboBoxDemo.java和这里: https : //docs.oracle.com/javase/tutorial/ uiswing/examples/components/

Why is using a wild card with a Java import statement bad?为什么在 Java 导入语句中使用通配符不好?

If you're using an IDE (which you should be doing), and there are more code owners than just you, using wildcard imports is bad because it:如果您使用的是 IDE(您应该这样做),并且除了您之外还有更多的代码所有者,那么使用通配符导入是不好的,因为它:

  • conceals information from the rest of the team向团队其他成员隐瞒信息
  • provides only false benefits (things which are better-solved using IDE functionality than with wildcard imports) to you as an individual仅向您个人提供虚假的好处(使用 IDE 功能比使用通配符导入更好地解决的问题)

Most of the "use wildcards" proponents have a focus on the individual: I don't want to maintain the list, I don't want see the clutter, etc. Here are several of the common examples:大多数“使用通配符”的支持者都关注个人:我不想维护列表,我不想看到混乱等等。下面是几个常见的例子:

  • maintenance is harder – when you want to introduce a new class into your source code, you have to manually add the import statement维护更难——当你想在你的源代码中引入一个新类时,你必须手动添加导入语句
  • refactoring is more difficult – if code is moved around, then import statements have to be updated重构更加困难——如果代码被移动,那么导入语句必须被更新
  • reduce clutter, tidy up file contents – goal here is something along the lines of "removing distractions"减少混乱,整理文件内容——这里的目标是“消除干扰”

These arguments were more convincing before IDEs did all of that automatically.在 IDE 自动完成所有这些之前,这些论点更有说服力。 If you're using a plain text editor instead of an IDE, then these arguments have some merit.如果您使用的是纯文本编辑器而不是 IDE,那么这些参数有一些优点。 But if you're using a plain text editor, you are already subjecting yourself to a number of other much more significant inefficiencies, and managing import statements is just one among many things that you should stop doing by hand.但是,如果您使用的是纯文本编辑器,那么您已经使自己遭受了许多其他更严重的低效率,并且管理导入语句只是您应该停止手动执行的众多事情之一。 IDEs offer automatic management of imports, powerful refactoring tools, and folding (hiding) of any parts of the code you don't want to see. IDE 提供导入的自动管理、强大的重构工具以及折叠(隐藏)您不想看到的任何代码部分。

For the "avoid wildcards" proponents, there are many examples, but I'll point out only one:对于“避免通配符”的支持者,有很多例子,但我只指出一个:

  • clarity – specifically, when someone new enters the codebase.清晰度——特别是当有人新进入代码库时。 They will arrive with questions, and continue to discover new questions as they explore the code.他们将带着问题到达,并在探索代码时继续发现新问题。 For this new code contributor, wildcard import statements do not answer any questions, and at worst can produce confusion, misunderstanding, new questions.对于这个新的代码贡献者,通配符导入语句不会回答任何问题,最坏的情况是会产生混乱、误解、新问题。 In contrast, with explicit imports (and using an IDE) the worst case is neutral: no new info provided;相比之下,使用显式导入(并使用 IDE)最坏的情况是中性的:没有提供新信息; at best, it not only reduces ambiguity but it can also provide answers.充其量,它不仅可以减少歧义,还可以提供答案。

At the end of the day, it helps the entire team to reduce (albeit in a small way) code complexity, to reduce confusion, to add clarity.归根结底,它可以帮助整个团队降低(尽管是很小的方式)代码复杂性,减少混乱,增加清晰度。

Importing all the classes in a package is considered a blind approach.导入包中的所有类被认为是一种盲目的方法。 A major reason for this is that it clutters the class namespace and could lead to conflicts between classes in different packages with the same name.造成这种情况的一个主要原因是它使类命名空间变得混乱,并可能导致不同包中具有相同名称的类之间发生冲突。

Specifically populating the necessary classes avoids that problem and clearly shows which versions were wanted.专门填充必要的类可以避免这个问题,并清楚地显示需要哪些版本。 This is good for code maintainability.这有利于代码的可维护性。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM