为什么在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交互。 如果使用通配符方法导入这两种情况,则会发生以下三种情况之一:

  1. 你在java.awt.Eventcom.mycompany.calendar.Event之间有一个彻底的命名冲突,所以你甚至不能编译。
  2. 你实际上只pipe理一个(只有两个导入中的一个是.* ),但这是错误的,你很难弄清楚为什么你的代码声称这个types是错误的。
  3. 当你编译你的代码时,没有com.mycompany.calendar.Event ,但是当他们稍后添加一个以前有效的代码时,突然停止编译。

明确列出所有导入的优点是,我可以一目了然地告诉您要使用哪个类,这使得读取代码更容易。 如果你只是做一个快速的一次性的事情,没有什么明显的错误 ,但未来的维护者会感谢你的清晰度,否则。

这是明星import的投票。 导入语句是为了导入一个 ,而不是一个类。 import整个包装要干净得多。 这里确定的问题(例如java.sql.Date vs java.util.Date )很容易通过其他方式进行补救,而不是真正由具体的import来解决,当然不能certificate所有类别都疯狂地迂腐。 没有什么比打开一个源文件更困难了,不得不翻页浏览100个导入语句。

进行特定的导入会使重构变得更加困难。 如果删除/重命名某个类,则需要删除其所有特定的导入。 如果将实现切换到同一个包中的不同类,则必须修复导入。 虽然这些额外的步骤可以实现自动化,但实际上它们确实是生产率的下降。

如果Eclipse没有默认进行类input,那么每个人都仍然在进行明星input。 我很抱歉,但是做具体的import真的没有合理的理由。

以下是如何处理课堂冲突:

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

请参阅我的文章按需导入是邪恶的

简而言之,最大的问题是当你将一个类添加到你导入的包中时,你的代码可能会中断。 例如:

 import java.awt.*; import java.util.*; // ... List list; 

在Java 1.1中,这很好, 列表在java.awt中find,并没有冲突。

现在假设你检查完全有效的代码,一年后有人把它带出来编辑它,并使用Java 1.2。

Java 1.2增加了一个名为List的接口到java.util。 繁荣! 冲突。 完美的工作代码不再有效。

这是一个EVIL语言function。 没有理由代码应该停止编译只是因为一个types被添加到一个包…

另外,读者很难确定你使用的是哪个“Foo”。

用Java导入语句使用通配符不错。

在Clean Code中 ,Robert C. Martin实际上build议使用它们来避免长时间的导入列表。

这是build议:

J1:通过使用通配符避免长时间导入列表

如果你使用一个包中的两个或更多的类,那么用整个包导入

import包。

长长的import清单令读者望而生畏。 我们不想用80行的import来混乱我们的模块。 相反,我们希望导入是关于我们与哪些软件包进行协作的简明声明。

具体的导入是硬依赖,而通配符导入不是。 如果您专门导入一个类,那么该类必须存在。 但是,如果您使用通配符导入包,则不需要存在特定的类。 导入语句只是在查找名称时将包添加到searchpath。 所以这样的导入没有创build真正的依赖关系,因此它们保证我们的模块不太耦合。

有时候,特定import的长列表是有用的。 例如,如果您正在处理遗留代码,并且想要查找需要构build模拟和存根的类,则可以沿着特定导入列表查找所有这些类的真正合格名称,然后将适当的存根。 但是,这种用于特定import的情况非常less见。 此外,大多数现代IDE将允许您使用单个命令将通配导入转换为特定导入列表。 所以即使在传统的情况下,最好导入通配符。

通配符导入有时会导致名称冲突和含糊不清。 两个具有相同名称但具有不同包装的类将需要专门导入,或者至less在使用时具有专门的资格。 这可能是一个麻烦,但是很less见,使用通配符导入仍然通常比特定的导入更好。

它混乱你的名字空间,要求你完全指定任何不明确的类名。 最常见的情况是:

 import java.util.*; import java.awt.*; ... List blah; // Ambiguous, needs to be qualified. 

它也有助于使你的依赖具体化,因为你所有的依赖都列在文件的顶部。

性能 :字节码相同,对性能没有影响。 尽pipe这会导致一些编译开销。

编译 :在我的个人计算机上,编译一个空白的类而不导入任何东西需要100毫秒,但是当导入java。*需要170毫秒时才是相同的类。

  1. 它有助于识别类名冲突:不同包中的两个类具有相同的名称。 这可以用*导入来掩盖。
  2. 它使得依赖关系是明确的,这样任何需要阅读你的代码的人都会知道你想要导入什么,以及你不想导入什么。
  3. 它可以使编译速度更快,因为编译器不必search整个程序包来识别错误,虽然这对于现代编译器通常不是什么大问题。
  4. 显式导入的不便之处在于使用现代IDE最小化。 大多数IDE允许您折叠导入部分,所以它不会妨碍,在需要时自动填充导入,并自动识别未使用的导入以帮助清理导入部分。

我工作过的大多数地方都使用任何大量的Java使显式导入成为编码标准的一部分。 我有时仍然使用*进行快速原型devise,然后在生成代码时扩展导入列表(某些IDE也会为您执行此操作)。

我更喜欢特定的导入,因为它允许我在不查看整个文件的情况下查看文件中使用的所有外部引用。 (是的,我知道它不一定会显示完全合格的参考资料,但是我尽可能避免。

在之前的一个项目中,我发现从* -import更改为特定的导入将编译时间减less了一半(从大约10分钟到大约5分钟)。 * -import使编译器search列出的每个软件包以匹配您所使用的类。 虽然这个时间可以很短,但是为大项目添加了。

* -import的副作用是开发人员会复制和粘贴常见的导入行,而不是考虑他们需要什么。

在DDD书

在任何开发技术的实现将基于,寻找方法,尽量减less重构模块的工作。 在Java中,不会导入到单独的类中,但是您至less可以一次导入整个包,这反映了包的高度内聚性,同时减less了更改包名的工作。

如果混淆了本地命名空间,那么它不是你的错 – 将这个包的大小归咎于它。

最重要的是导入java.awt.*可以使您的程序与未来的Java版本不兼容:

假设您有一个名为“ABC”的类,您使用的是JDK 8,并且您导入了java.util.* 。 现在,假设Java 9出来了,它有一个包java.util的新类,碰巧也被称为“ABC”。 你的程序现在不能在Java 9上编译,因为编译器不知道名字“ABC”是指你自己的类还是java.awt的新类。

如果仅从实际使用的java.awt中显式导入那些类,则不会出现该问题。

资源:

Java导入