【发布时间】:2010-09-13 22:20:23
【问题描述】:
使用像这样的单个语句更方便、更简洁
import java.awt.*;
而不是导入一堆单独的类
import java.awt.Panel;
import java.awt.Graphics;
import java.awt.Canvas;
...
在import 语句中使用通配符有什么问题?
【问题讨论】:
使用像这样的单个语句更方便、更简洁
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,但是当他们后来添加一个时,您之前有效的代码突然停止编译。显式列出所有导入的好处是我可以一眼看出您打算使用哪个类,这使得阅读代码变得更加容易。如果您只是做一件快速的一次性事情,那么没有什么明显错误,但未来的维护者会感谢您的清晰说明。
【讨论】:
这是对明星进口的投票。导入语句旨在导入包,而不是类。导入整个包要干净得多;此处确定的问题(例如 java.sql.Date 与 java.util.Date)很容易通过其他方式解决,而不是真正通过特定的导入解决,当然也不能证明对所有类都进行疯狂迂腐的导入。没有什么比打开一个源文件并不得不翻阅 100 个导入语句更令人不安的了。
进行特定的导入会使重构更加困难;如果你删除/重命名一个类,你需要删除它的所有特定的导入。如果将实现切换到同一个包中的不同类,则必须修复导入。虽然这些额外的步骤可以自动化,但它们确实是对生产力的打击,并没有真正的收益。
如果默认情况下 Eclipse 不执行特定的类导入,那么每个人仍然会执行星型导入。很抱歉,但确实没有合理的理由进行特定的导入。
以下是处理类冲突的方法:
import java.sql.*;
import java.util.*;
import java.sql.Date;
【讨论】:
Foo,并且如果我在不使用 IDE 的情况下阅读您的代码(因为您的论点是我不应该使用一),我怎么知道Foo来自哪个包?当然,使用 IDE,IDE 会告诉我,但您的整个论点是我应该能够在没有 IDE 的情况下阅读代码。进行显式导入有助于记录代码 (避免使用通配符的好理由),而且我更有可能阅读代码在不使用 IDE 的情况下,我将在不使用 IDE 的情况下编写代码。
请看我的文章Import on Demand is Evil
简而言之,最大的问题是当一个类添加到你导入的包中时你的代码可能会中断。例如:
import java.awt.*;
import java.util.*;
// ...
List list;
在 Java 1.1 中,这很好;在java.awt中找到list,没有冲突。
现在假设您签入了完美运行的代码,一年后有人将其拿出来进行编辑,并且正在使用 Java 1.2。
Java 1.2 向 java.util 添加了一个名为 List 的接口。繁荣!冲突。完美运行的代码不再有效。
这是一个 EVIL 语言功能。 NO 原因是代码应该停止编译只是因为类型被添加到包中......
此外,读者很难确定您使用的是哪个“Foo”。
【讨论】:
java.util.List vs java.awt.List 不太难弄清楚,但是当类名是 Configuration 并且多个依赖库已将其添加到最新的 maven repo 版本中时尝试一下。
在 Java 导入语句中使用通配符不错。
在Clean Code 中,Robert C. Martin 实际上建议使用它们来避免长导入列表。
这里是推荐:
J1:通过使用避免长导入列表 通配符
如果您使用两个或多个类 包,然后导入整个包 与
导入包。*;
长长的进口清单令人望而生畏 读者。我们不想混乱 以 80 位居我们模块的首位 进口线。而是我们想要 进口是一个简洁的陈述 关于我们合作的软件包 与。
具体的进口很难 依赖项,而通配符导入 不是。如果您专门导入一个 类,则该类必须存在。但 如果你导入一个包 通配符,不需要特定的类 存在。导入语句很简单 将包添加到搜索路径 在寻找名字时。所以不是真的 依赖是由此类导入创建的, 因此,它们有助于保持我们的 模块耦合较少。
有时候,长长的列表 特定的导入可能很有用。为了 例如,如果您正在处理 遗留代码,你想知道 你需要什么类来构建模拟 和存根,你可以走下来 要查找的特定进口清单 所有这些的真实限定名 类,然后把适当的 存根到位。然而,这种用于 具体进口非常少见。 此外,大多数现代 IDE 将 允许您转换通配符 进口到特定进口清单 用一个命令。所以即使在 遗留案例最好导入 通配符。
通配符导入有时会导致 名称冲突和歧义。二 具有相同名称的类,但在 不同的包,将需要 专门进口的,或至少 使用时特别合格。这 可能会令人讨厌,但很少见 使用通配符导入仍然是 一般比具体好 进口。
【讨论】:
性能:对性能没有影响,因为字节码相同。 虽然这会导致一些编译开销。
编译:在我的个人机器上,编译一个空白类而不导入任何东西需要 100 毫秒,但导入 java.* 时相同的类需要 170 毫秒。
【讨论】:
import java.* 不导入任何内容。为什么会有所作为?
它会使您的命名空间变得混乱,要求您完全指定任何不明确的类名。最常见的情况是:
import java.util.*;
import java.awt.*;
...
List blah; // Ambiguous, needs to be qualified.
它还有助于使您的依赖项具体化,因为您的所有依赖项都列在文件的顶部。
【讨论】:
我工作过的大多数使用大量 Java 的地方都将显式导入作为编码标准的一部分。我有时仍然使用 * 进行快速原型设计,然后在产品化代码时扩展导入列表(某些 IDE 也会为您执行此操作)。
【讨论】:
我更喜欢特定的导入,因为它允许我查看文件中使用的所有外部引用,而无需查看整个文件。 (是的,我知道它不一定会显示完全限定的引用。但我尽可能避免使用它们。)
【讨论】:
在之前的项目中,我发现从 *-imports 更改为特定的导入将编译时间减少了一半(从大约 10 分钟到大约 5 分钟)。 *-import 使编译器在列出的每个包中搜索与您使用的类匹配的类。虽然这个时间可能很小,但对于大型项目来说,它加起来。
*-import 的一个副作用是开发人员会复制和粘贴常见的导入行,而不是考虑他们需要什么。
【讨论】:
无论实现将基于何种开发技术,寻找方法来最小化 重构 MODULES 的工作。在 Java 中,无法避免导入单个类,但是您 至少可以一次导入整个包,反映包是高内聚单元的意图 同时减少更改包名称的工作量。
如果它使本地命名空间混乱,这不是你的错 - 归咎于包的大小。
【讨论】:
没有运行时影响,因为编译器会自动将 * 替换为具体的类名。如果你反编译 .class 文件,你永远不会看到import ...*。
C# always 使用 *(隐式),因为您只能使用 using 包名。您根本无法指定类名。 Java 引入了 c# 之后的特性。 (Java 在许多方面都非常棘手,但它超出了这个主题)。
在 Intellij Idea 中,当您执行“组织导入”时,它会自动将同一包的多个导入替换为 *。这是一项强制性功能,因为您无法将其关闭(尽管您可以提高阈值)。
已接受回复列出的案例无效。没有 * 你仍然有同样的问题。无论您是否使用 *,您都需要在代码中指定 pakcage 名称。
【讨论】:
最重要的是,导入java.awt.*会使你的程序与未来的Java版本不兼容:
假设您有一个名为“ABC”的类,您正在使用 JDK 8 并导入 java.util.*。现在,假设 Java 9 出现了,它在包java.util 中有一个新类,巧合的是,它也被称为“ABC”。您的程序现在无法在 Java 9 上编译,因为编译器不知道名称“ABC”是指您自己的类还是 java.awt 中的新类。
当您只从java.awt 显式导入您实际使用的那些类时,您不会遇到这个问题。
资源:
【讨论】:
Stream 作为在 Java 8 中的 java.util 中添加的新类的示例...
以下是我发现的有关此主题的一些内容。
在编译期间,编译器会尝试从 .* 导入中查找代码中使用的类,并通过从 .* 导入中选择使用的类来生成相应的字节码。所以使用 .* import 或 .class names import 的字节码是一样的,因为相同的字节码,运行时的性能也会一样。
在每次编译中,编译器都要扫描 .* 包的所有类,以匹配代码中实际使用的类。因此,与使用 .class 名称导入相比,使用 .* 导入的代码在编译过程中需要更多时间。
使用 .* 导入有助于使代码更简洁
当我们使用来自两个不同包的两个同名类时,使用 .* 导入会产生歧义。例如,日期在两个包中都可用。
import java.util.*;
import java.sql.*;
public class DateDemo {
private Date utilDate;
private Date sqlDate;
}
【讨论】:
在双方提出的所有有效观点中,我还没有找到避免使用通配符的主要原因:我希望能够阅读代码并直接知道每个类是什么,或者它的定义是否不在语言或文件,在哪里可以找到它。如果使用 * 导入了多个包,我必须搜索每个包以找到我不认识的类。可读性是至高无上的,我同意代码不应该需要一个 IDE 来阅读它。
【讨论】:
记录在案: 当你添加一个导入时,你也表明了你的依赖关系。
您可以很快看到文件的依赖关系(不包括相同命名空间的类)。
【讨论】:
忘掉混乱的命名空间...想想那些不得不阅读和理解你在 GitHub、vi、Notepad++ 或其他非 IDE 文本编辑器中的代码的可怜人。
那个人必须煞费苦心地查找来自一个通配符的每个标记与每个通配符范围内的所有类和引用......只是为了弄清楚到底发生了什么。
如果您只为编译器编写代码 - 并且您知道自己在做什么 - 我相信通配符没有问题。
但如果其他人——包括未来的你——想在一次阅读中快速理解特定的代码文件,那么显式引用会有很大帮助。
【讨论】:
使用通配符导入还不错,因为 Oracle 的 Java 教程使用通配符导入。我不认为 Oracle 的 Java 人员会做错事。
上述程序使用通配符导入:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
您可以在此处查看更多程序: https://docs.oracle.com/javase/tutorial/uiswing/examples/components/.
【讨论】:
导入包中的所有类被认为是一种盲目的方法。造成这种情况的主要原因是它使类命名空间变得混乱,并可能导致不同包中同名的类之间发生冲突。
专门填充必要的类可以避免这个问题,并清楚地显示需要哪些版本。这有利于代码的可维护性。
【讨论】: