【问题标题】:Java: How to reference library dependencies *without* 'import' statement?Java:如何引用库依赖关系*没有*'import'语句?
【发布时间】:2016-02-14 06:18:45
【问题描述】:

我正在使用 NetBeans,并且正在将常用类分解到一个库中。我想引用库中的类。通过告诉 NetBeans 一个项目依赖于另一个项目,我可以很容易地做到这一点,后者是一个 Java 库项目。

库项目名为“ReVueLib”。我有一个名为“Stream”的文件夹,其中有一个类“StreamClient”。以下是重要内容的精简版:

package Stream;
class StreamClient {
    public void StreamClient() {}
};

我还有一个名为“ReVueServer”的项目,主要看起来是这样的:

package revueserver;
import Stream.StreamClient;
public class ReVueServer {
    public static void main(String[] args) {
        Class c = StreamClient.class;
        System.out.println(c.getName());
    }
};

现在,如果我运行它,它可以正常工作并打印出“Stream.StreamClient”。但是说我想引用这个类没有导入。原因是在另一个项目中,我想将 Network.StreamClient 子类化为一个名为 StreamClient 的类。这将帮助我避免重构大量现有代码。换句话说,我正在利用命名空间分离来继承和重用旧名称。

不幸的是,如果我取出导入,“Stream.StreamClient”不起作用,“ReVueLib.Stream.StreamClient”或其他我尝试过的东西也不起作用。

我需要使用什么 FQCN 来引用这个库类而不“导入”它?

【问题讨论】:

  • 使用类的完全限定名。

标签: java netbeans import


【解决方案1】:

包中类的专有名称包括包的全名。 import 语句让您避免编写前缀,但这是一个编译时技巧。这就是为什么您的程序会打印完全限定名称 Stream.StreamClient,即使您的程序通过其短名称 StreamClient 引用该类。

您始终可以重写您的程序以使用所有类的全名:

Class c = Stream.StreamClient.class;

甚至

java.lang.Class c = Stream.StreamClient.class;

【讨论】:

  • 听上去,他还导入了Stream,所以编译器可能搞糊涂了。 编写的代码可以工作,尽管“Stream”作为包名往往会违反大多数约定。
【解决方案2】:

Stream.StreamClient 是 FQCN,您应该能够删除导入并使用它。

【讨论】:

    【解决方案3】:

    我认为您需要一个比“不起作用”更好的解释。为什么这不起作用?

    package revueserver;
    public class ReVueServer {
        public static void main(String[] args) {
            Class c = Stream.StreamClient.class;
            System.out.println(c.getName());
        }
    }
    

    【讨论】:

      【解决方案4】:

      我会支持很多好的答案。事实证明,这个问题是虚假的。我有一个命名冲突。在尝试添加对 Stream 命名空间的引用之前,已经有一个 Stream 类我忘记删除了。类名优先于外部命名空间。

      【讨论】:

        【解决方案5】:

        您不能在不导入类 API 的情况下引用它(通过导入或显式完全限定引用)。曾经。那是一成不变的。

        我认为您的误解是您可以在某处编写“Stream.class”,并且取决于类路径上的内容,它将起作用。这只在非常有限的程度上是正确的。虽然您可以将 Stream.class 替换为具有相同限定名称和 API(即方法和引用字段)的另一个类,但类始终由它们的限定名称 (那是包+类名)。

        您的问题听起来更像是您需要使用通用 API 进行依赖注入。这允许您选择同一 API 的不同实现

        这通常通过将 API 定义为接口或抽象类来实现:

        package my.common;
        
        public interface MyAPI {
            public void doSomething();
        }
        

        您的程序代码只能在定义的接口上运行,您需要类路径上的接口才能编译和运行。

        然后,您可以在任何地方定义任意数量的接口实现:

        package my.green;
        
        public GreenAPI implements MyAPI {
            public void doSomething() {
                System.out.println("You reached green API");
            }
        }
        

        最后,您的程序需要被注入如何在运行时到达实现的信息。在最简单的情况下,这可以通过将类名作为参数传递给某处来完成,也有一些框架可以做到这一点:

        package something;
        
        import my.common.MyAPI;
        
        public MyAPIUser {
            public static void main(String[] args) {
                try {
                    Class<?> apiImpl = Class.forName("my.green.GreenAPI");
                    MyAPI api = (MyAPI) apiImpl.newInstance();
                    api.doSomething();
                } catch (Exception e) {
                    e.printStakcTrace();
                }
            }
        }
        

        顺便说一下,JRE 如何为所有 JDBC 驱动程序提供通用 API。

        【讨论】:

        • 你可以完全引用一个类而不导入它,只需使用完全限定名称:'java.util.Map someMap = new java.util.HashMap();'无需任何导入语句即可工作。例如,如果您想使用两个具有相同名称的类(即 java.util.List 和 java.awt.List),您将需要它
        • @Torque 那是不导入它。生成的类文件将具有与您导入它时完全相同的依赖关系。它是纯粹的编译器糖,它不会改变你对运行时依赖项的看法。
        • 导入只是编译器的事情。使用 FQCN 意味着不必导入它。生成的字节码看起来一样吗?是的。但是使用 FQCN 是引用一个类而不是导入它。
        • “顺便说一下,JRE 如何设法为所有 JDBC 驱动程序提供通用 API。”如果您指的是自 1.4 以来就不需要的 Class.forName() 调用。否则那句话就没有意义了。
        • 编程术语具有精确的含义,因此,是的,我“坚持”确切的含义,因为单词意味着事情。而且您的 JDBC 评论仍然没有意义,但是哦。我想不出有任何驱动程序支持 JDBC 4,因为它已经问世十多年了。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-04-20
        • 2021-10-21
        • 2010-10-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-07-22
        相关资源
        最近更新 更多