【问题标题】:How to refer to a class when both simple and fully-qualified names clash当简单名称和完全限定名称发生冲突时如何引用一个类
【发布时间】:2018-05-31 22:39:16
【问题描述】:

考虑以下病态的例子:

class Ideone {
  static class ArrayList<T> {
    ArrayList() {
      System.out.println("!!");
    }
  }

  static class java {
    static class util {
      static class ArrayList<T> {
        ArrayList() {
          System.out.println("Here");
        }
      }
    }
  }

  public static void main(String[] args) {
    new ArrayList<>();
    new java.util.ArrayList<>();
    // Can I refer to the "usual" java.util.ArrayList?
  }
}

在构造函数中创建的两个实例属于嵌套类。

但是我怎么能提到我们在同一个班级里都知道和喜爱的java.util.ArrayList?我们不能导入它,也不能使用完全限定名称,因为会使用嵌套类符号。

在这种情况下我们能做什么? (除了显而易见的 - 不要为嵌套类使用这种肆无忌惮的邪恶名称)。

【问题讨论】:

  • 你有理由相信有一些解决方案吗?
  • @shmosel 不是特别好。我只是在想一些奇怪的进口案例,同时进行一些容易出错的检查。
  • 我很想看到一位语言架构师回答这个问题。
  • 如果你遵循标准Java Naming Conventions,这个问题就不会发生,即包名全部小写,类名混合大小写。命名约定的存在是有原因的。
  • @Andreas 我同意;但我问的是语言允许什么,而不是约定使用什么。

标签: java language-specifications


【解决方案1】:

如果您已经完成了 2 件事,则不能再直接引用 java.util.ArrayList

  1. 使用范围内的静态嵌套类隐藏简单名称 ArrayList
  2. 隐藏完全限定名称java.util.ArrayList,其中类ArrayList 嵌套在类util 中,嵌套在嵌套类java 中。

您甚至不能“拆分”导入以尝试使用“部分限定”导入。

import java.*;

...

// This doesn't work!
new util.ArrayList<>();

你可以import java.*;,但那毫无价值; java 包中没有直接定义任何类。

但是,您可以间接引用类 java.util.ArrayList,因为它不是 final。在类Ideone 的范围之外,声明一个具有不同名称的子类。

class AnArrayList<T> extends java.util.ArrayList<T> {}

然后你可以参考那个类和程序到接口:

List<Integer> al = new AnArrayList<>();  // won't print !! or Here

【讨论】:

  • 当然,您可以使用Stream.of().collect(Collectors.toList()) 之类的方式获得实际 java.util.ArrayList
  • @AndyTurner 听起来您应该回答自己的问题。我查找了Collectors.toList 的源代码,它使用了ArrayList::new,证实了您的说法。
  • 也许吧。这并不是我要问的问题的真正答案,即您能否引用 type,而不是如何获取该类型的实例。
【解决方案2】:

我要冒昧地说在Ideone 内无法引用它。我看到的唯一解决方案是创建另一个类Idetwo(它是Ideone 的嵌套对象(即它驻留在同一个文件中))并从那里返回一个java.util.ArrayList 以在Ideone 中使用:

import java.util.ArrayList;

class Ideone {
    ...
}

class Idetwo {
    static ArrayList<Integer> getList() {
        ArrayList<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        return list;
    }
}

您只需将Ideone 的主要方法更改为:

public static void main(String[] args) throws Exception {
    new Ideone.ArrayList<>();
    new Ideone.java.util.ArrayList<>();
    Idetwo.getList().forEach(System.out::println);
}

输出:

!!
Here
1
2
3

注意:使用这种方法,导入java.util.ArrayList 可以正常工作。但是,如果您要在 Ideone 的 main 方法中调用 List&lt;Integer&gt; list = Idetwo.getList();,则需要导入 java.util.*,因为单独导入将不起作用(有趣)。

【讨论】:

  • 不错的尝试,但我说的是“在同一个班级”,而不是“在同一个编译单元”;)
  • @AndyTurner Darn,错过了;)
【解决方案3】:

你试过了吗

Class cls = ClassLoader.getSystemClassLoader().loadClass("java.util.ArrayList");
List arrayList = cls.newInstance();

很久没有考虑类加载器了,但是 IIRC .loadClass() 会优先尝试从最基础的类加载器加载,真正的 java.util 包应该由引导类加载器提供,它给了它优先级高于您在应用程序中定义的任何内容。

【讨论】:

  • 如果您调用 loadClass 而不是 findClass(即 protected),这应该可以工作。
【解决方案4】:

这就是为什么包以小写字母开头并以大写字母开头的原因,因为very first edition of the official code conventions for java。我想我从来没有见过 java 代码打破这个约定。

但是您可以使用它的完全限定名称从外部引用内部类型:

com.whatever.bad.project.SomeClass.java.util.ArrayList

当您必须从内部引用外部类型时,您可以更改该源文件以符合 Java 命名准则。

【讨论】:

  • 请参考我对@Andreas 评论的回复:“我问的是语言允许的内容,而不是约定使用的内容”。
猜你喜欢
  • 1970-01-01
  • 2012-10-12
  • 2021-06-03
  • 2021-01-17
  • 2010-11-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多