使用单个语句更方便,更清晰
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.sql.Date
vs java.util.Date
)很容易通过其他方式弥补,而不是通过具体的进口来解决,当然也不能证明所有类别的疯狂迂腐进口都是正当的.没有什么比打开源文件和必须翻阅100个import语句更令人不安的了.
进行特定的导入会使重构变得更加困难; 如果删除/重命名类,则需要删除其所有特定的导入.如果将实现切换到同一个包中的其他类,则必须修复导入.虽然这些额外的步骤可以实现自动化,但它们确实可以提高生产效率.
即使Eclipse默认不进行类导入,每个人仍然会进行星级导入.对不起,但是进行特定进口确实没有合理的理由.
以下是如何处理类冲突:
import java.sql.*; import java.util.*; import java.sql.Date;
请参阅我的文章Import on Demand is Evil
简而言之,最大的问题是当一个类被添加到您导入的包时,您的代码可能会中断.例如:
import java.awt.*; import java.util.*; // ... List list;
在Java 1.1中,这很好; 列表在java.awt中找到,没有冲突.
现在假设您签入了完美的代码,一年后其他人将其编辑出来进行编辑,并使用Java 1.2.
Java 1.2在java.util中添加了一个名为List的接口.繁荣!冲突.完美的代码不再有效.
这是EVIL语言功能.有否原因,代码应该停止编译仅仅因为一个类型添加到包...
此外,它使读者难以确定您正在使用哪个"Foo".
这不是不好用通配符与Java导入语句.
在清洁代码中,Robert C. Martin实际上建议使用它们来避免长导入列表.
以下是建议:
J1:使用通配符避免长导入列表
如果您使用包中的两个或更多类,则使用导入整个包
import package.*;
很长的进口清单令读者望而生畏.我们不希望使用80行导入来混淆模块的顶部.相反,我们希望导入是关于我们与哪些软件包协作的简明陈述.
特定导入是硬依赖性,而通配符导入则不是.如果您专门导入一个类,那么该类必须存在.但是如果导入带有通配符的包,则不需要存在特定的类.在搜索名称时,import语句只是将包添加到搜索路径中.因此,这些导入不会产生真正的依赖关系,因此它们可以使我们的模块更少耦合.
有时,特定导入的长列表可能很有用.例如,如果您正在处理遗留代码,并且想要找出构建模拟和存根所需的类,则可以在特定导入列表中查找所有这些类的真正限定名称然后放入适当的存根.但是,这种用于特定进口的用途非常罕见.此外,大多数现代IDE允许您使用单个命令将通配符导入转换为特定导入列表.因此,即使在传统情况下,最好导入通配符.
通配符导入有时会导致名称冲突和歧义.具有相同名称但在不同包中的两个类需要专门导入,或者至少在使用时特别限定.这可能是令人讨厌的,但是很少见,使用通配符导入通常仍然比特定导入更好.
它使您的命名空间变得混乱,要求您完全指定任何不明确的类名.最常见的情况是:
import java.util.*; import java.awt.*; ... List blah; // Ambiguous, needs to be qualified.
它还有助于使您的依赖项具体化,因为所有依赖项都列在文件的顶部.
性能:字节代码相同,对性能没有影响.虽然它会导致一些编译开销.
编译:在我的个人机器上,编译一个空白类而不导入任何东西需要100毫秒,但导入java时相同的类.*需要170毫秒.
它有助于识别类名冲突:不同包中具有相同名称的两个类.这可以用*import掩盖.
它使依赖项显式化,以便以后必须阅读代码的任何人都知道您要导入的内容以及您不想导入的内容.
它可以使编译速度更快,因为编译器不必搜索整个包来识别依赖性,尽管这对现代编译器来说通常不是很大.
使用现代IDE可以最大限度地减少显式导入的不便之处.大多数IDE允许您折叠导入部分,因此它不会妨碍,在需要时自动填充导入,并自动识别未使用的导入以帮助清理它们.
我工作过的大多数使用大量Java的地方都使得显式导入成为编码标准的一部分.我有时仍然使用*进行快速原型设计,然后在产品化代码时扩展导入列表(某些IDE也会为您执行此操作).
我更喜欢特定的导入,因为它允许我在不查看整个文件的情况下查看文件中使用的所有外部引用.(是的,我知道它不一定会显示完全合格的参考文献.但我尽可能避免它们.)
在之前的项目中,我发现从*-imports更改为特定导入会将编译时间缩短一半(从大约10分钟缩短到大约5分钟).*-import使编译器搜索列出的每个包中与您使用的类匹配的类.虽然这个时间可能很短,但它会增加大型项目.
*-import的一个副作用是开发人员会复制和粘贴常见的导入行,而不是考虑他们需要什么.
在DDD书中
在实现将基于的任何开发技术中,寻找最小化重构模块的工作的方法.在Java中,无法逃避导入到单个类中,但您至少可以一次导入整个包,这反映了包具有高度内聚单元的意图,同时减少了更改包名称的工作量.
如果它使本地命名空间混乱不是你的错 - 责怪包的大小.