【问题标题】:Why to define multiple imports?为什么要定义多个导入?
【发布时间】:2013-04-06 23:54:27
【问题描述】:

考虑以下导入语句:

导入 java.io.*; // 有意义

导入 javax.servlet.*;

导入 javax.servlet.http.*;

是不是我们已经包含了“import javax.servlet.;”因此它会自动包含另一个导入语句,即“import javax.servlet.http.;”?

为什么要为 http 明确定义“import javax.servlet.http.*”?

如果我错了,请澄清并告诉我。

【问题讨论】:

    标签: java import wildcard


    【解决方案1】:

    是的,您确实需要为每个包执行通配符导入。

    为什么?就 JLS 而言,“com.example”和“com.example.pkg”是不相关的包。 JLS 中提到了子包的概念,但没有相关的语义。特别是不在“访问”规则中。 JLS 7.1 说:

    "包的分层命名结构旨在方便以常规方式组织相关包,但除了禁止包具有与在该包中声明的顶级类型(第 7.6 节)。

    例如,一个名为oliver 的包与另一个名为oliver.twist 的包之间或名为evelyn.woodevelyn.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.weazellibcom.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 是模棱两可的......因为超级通配符导入的影响从以前不存在的包中提取类名。

    请注意,常规通配符导入也存在同样的问题。这就是为什么很多人不使用通配符导入的原因。但毫无疑问,超级通配符会使问题变得更糟。

    【讨论】:

    • 好吧,如果你不使用它们,为什么要导入整个“子包”树是有道理的。
    • @kellax - 我不明白你在说什么“有道理”。当前模型或具有“超级通配符”导入的模型。我的观点是,懒惰的程序员会不假思索地使用超级通配符……因为它减少了打字或其他东西。 (就像如果 Java 没有检查异常,他们会忽略异常处理。)面对库内部细节的变化,也存在稳定性问题。
    • 感谢您的回复,但是有没有官方的 java 文档明确表示子包确实存在,就像@kellax 提到的那样?
    • @Adarsh - 我错了。 JLS 确实使用术语子包。更新了 JLS 链接和报价。
    【解决方案2】:

    不,当您导入某些内容时,您只导入特定类,或者您导入包以及属于该包的所有类,而不是属于该包的子包的类。

    每个包都包含一些相关的类和子包。子包中的类不一定与父包中的类相关。因此,将它们也导入是没有意义的。这样可以避免不必要的导入并保持项目干净。

    示例:
    假设您正在为您的 GUI 应用程序构建一个 View 类,您可能希望将关注点分开,这样您的 View 中就会有 JComponents,而控制器中有 Listeners

    因此,在您的视图中,您将导入:import javax.swing.*; 这样您将获得所有 JComponent 类,但由于您的视图中不需要 event 包,因此 import 仅导入您实际需要的内容是有意义的.即使swingevent 包有些相关,也不需要导入事件。

    因此,当您在 Java 中导入某些内容时,您要么导入整个包 import javax.swing.*; 以及与包相关的所有类,要么导入包中的单个类 import javax.swing.JButton;

    您永远不会导入包含所有子包及其子包等的包。因为您很可能不需要所有这些。

    【讨论】:

      【解决方案3】:

      在规范中定义了导入的工作方式。

      Java Language Spec

      原因是javax.servlet和javax.servlet.http是不同的包,import *只带入包成员。

      此外,通配符导入也不是一个好主意,因为它会降低代码的可读性。

      【讨论】:

        猜你喜欢
        • 2011-09-09
        • 2016-02-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-11-23
        • 1970-01-01
        • 2022-01-14
        相关资源
        最近更新 更多