是的,您确实需要为每个包执行通配符导入。
为什么?就 JLS 而言,“com.example”和“com.example.pkg”是不相关的包。 JLS 中提到了子包的概念,但没有相关的语义。特别是不在“访问”规则中。 JLS 7.1 说:
"包的分层命名结构旨在方便以常规方式组织相关包,但除了禁止包具有与在该包中声明的顶级类型(第 7.6 节)。
例如,一个名为oliver 的包与另一个名为oliver.twist 的包之间或名为evelyn.wood 和evelyn.waugh 的包之间没有特殊的访问关系。也就是说,名为oliver.twist 的包中的代码对oliver 中声明的类型的访问没有比任何其他包中的代码更好的了。”
(并且允许导入许多 不相关 包的结构会产生不良后果......见下文。)
但是为什么?因为这就是语言的设计方式。
但是为什么?您需要询问 Java 语言设计团队,他们在 1990 年代初期做出设计决策时的想法。
但也许我们可以看到如果有一个多包通配符导入会发生什么。
考虑一下这个包结构,这是一种非常常见的模式:
com.example.weazellib - contains the public API classes for the library
com.example.weazellib.impl - contains implementation classes that
shouldn't be used by external clients
众所周知,程序员很懒(很多人都懒),所以有些程序员可能会这样写:
import com.example.weazellib.** // hypothetical syntax
他/她现在将在这个类命名空间中同时拥有外部 API 类和内部类,并且很容易不小心创建对内部的依赖。
(在你说“将内部类包私有”之前......这是行不通的。com.example.weazellib 中的类需要能够使用com.example.weazellib.impl 中的类. 如果后者是包私有的,那么前者将无法使用它们。)
相比之下,在 Java 没有通配符导入包“树”的世界中,您不能意外地这样做。您必须故意导入impl 包。这是一件好事,并且比为多个包编写通配符导入的“不便”重要得多。
另一个问题是通配符导入不利于源代码的长期稳定性,而超级通配符会使情况变得更糟。
假设程序员决定在他的代码中同时导入com.example.weazellib 和com.example.weazellib.impl 是正确的做法......并使用超级通配符来导入两者。并假设他编写代码以使用com.example.weazellib.impl.ToesImpl ... 作为ToesImpl。
现在考虑如果“weazellib”开发人员添加第三个包com.example.weazellib.impl2,其中包含替代实现类......与impl中的类具有相同的简单名称会发生什么;例如我们现在有这样的类:
com.example.weazellib.impl.ToesImpl
com.example.weazellib.impl2.ToesImpl
会发生什么?好吧,现在程序员代码中有一个编译错误。 ToesImpl 是模棱两可的......因为超级通配符导入的影响从以前不存在的包中提取类名。
请注意,常规通配符导入也存在同样的问题。这就是为什么很多人不使用通配符导入的原因。但毫无疑问,超级通配符会使问题变得更糟。