为什么在Java导入语句中使用通配符不好?
使用单个语句像
import java.awt.*;
而不是导入一堆单独的类
import java.awt.Panel; import java.awt.Graphics; import java.awt.Canvas; ...
在import语句中使用通配符有什么问题?
解决方案
唯一的问题是,它会使本地名称空间混乱。例如,假设我们正在编写一个Swing应用程序,因此需要java.awt.Event
,并且还与该公司的日历系统接口,该日历系统具有com.mycompany.calendar.Event
。如果我们同时使用通配符方法导入这两种情况,则会发生以下三种情况之一:
- 我们在java.awt.Event和com.mycompany.calendar.Event之间存在完全的命名冲突,因此甚至无法编译。
- 实际上,我们实际上只能导入一个(两个导入中只有一个执行"。*"),但这是错误的,并且我们很难弄清楚为什么代码声称类型是错误的。
- 当我们编译代码时,没有
com.mycompany.calendar.Event
,但是当他们以后添加一个代码时,我们先前有效的代码突然停止编译。
显式列出所有导入的优点是,我可以一目了然地告诉我们要使用哪个类,这使阅读代码变得更加容易。如果我们只是快速完成一项操作,那么就没有明显的错误,但是以后的维护人员将感谢清晰说明。
我更喜欢特定的导入,因为它使我可以查看文件中使用的所有外部引用,而无需查看整个文件。 (是的,我知道它不一定会显示完全合格的参考。但是我会尽可能避免使用它们。)
它会使名称空间混乱,要求我们完全指定任何模棱两可的类名。最常见的情况是:
import java.util.*; import java.awt.*; ... List blah; // Ambiguous, needs to be qualified.
由于所有依赖项都列在文件顶部,因此还有助于使依赖项具体化。
- 它有助于识别类名冲突:不同程序包中的两个类具有相同的名称。这可以用* import掩盖。
- 它使依赖关系明确,以便以后必须阅读代码的任何人都知道我们要导入的内容和我们不想要导入的内容。
- 它可以使某些编译速度更快,因为编译器不必搜索整个程序包来确定依赖关系,尽管对于现代编译器而言,这通常不是什么大问题。
- 显式导入的不便之处通过现代IDE得以最小化。大多数IDE允许我们折叠导入部分,以免妨碍导入,在需要时自动填充导入,并自动识别未使用的导入以帮助清理它们。
我工作过的大多数使用大量Java的地方都将显式导入作为编码标准的一部分。在生产代码时,有时我仍使用*进行快速原型制作,然后扩展导入列表(某些IDE也会为我们完成此操作)。
这是明星进口的投票。 import语句用于导入软件包,而不是类。导入整个软件包要干净得多。此处确定的问题(例如,java.sql.Date和java.util.Date)可以通过其他方式轻松解决,无法通过特定的导入方式真正解决,当然也不能为所有类的疯狂的学究式导入辩护。没有什么比打开源文件并翻阅100条import语句更令人不安的了。
进行特定的进口使重构更加困难;如果删除/重命名一个类,则需要删除其所有特定的导入。如果将实现切换到同一包中的其他类,则必须修复导入。尽管这些额外的步骤可以自动执行,但它们实际上是对生产力的打击,没有任何实际收益。
如果Eclipse默认不进行类导入,那么每个人仍将进行星型导入。很抱歉,但是确实没有合理的理由来进行特定的进口。
以下是处理类冲突的方法:
import java.sql.*; import java.util.*; import java.sql.Date;
请参阅我的文章按需导入是邪恶的
简而言之,最大的问题是,将类添加到导入的包时,代码可能会中断。例如:
import java.awt.*; import java.util.*; // ... List list;
在Java 1.1中,这很好。列表在java.awt中找到,没有冲突。
现在,假设我们签入了运行良好的代码,一年后,其他人将其带出进行编辑,并且正在使用Java 1.2.
Java 1.2在java.util中添加了一个名为List的接口。繁荣!冲突。完美工作的代码不再起作用。
这是一种EVIL语言功能。没有理由仅仅因为将类型添加到包中而停止编译代码了。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
另外,这使读者很难确定我们正在使用哪个" Foo"。
在先前的项目中,我发现从* -imports更改为特定的import可以将编译时间减少一半(从大约10分钟到大约5分钟)。 * -import使编译器在列出的每个软件包中搜索与我们所使用的软件包相匹配的类。尽管这段时间可能很小,但对于大型项目而言却是加总的。
- -import的副作用是,开发人员将复制并粘贴常见的导入行,而不用考虑他们的需求。
在Java import语句中使用通配符也不错。
在Clean Code中,Robert C. Martin实际上建议使用它们来避免冗长的导入列表。
这是建议:
J1: Avoid Long Import Lists by Using Wildcards 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. 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. 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. 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.