问题 如何在简单名称和完全限定名称冲突时引用类


考虑以下病理示例:

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 我们都知道并喜欢同一个班级?我们无法导入它,也不能使用完全限定名,因为将使用嵌套类符号。

在这种情况下我们能做些什么? (除了明显的 - 停止使用嵌套类的这些肆无忌惮的邪恶名称)。


9550
2018-05-31 22:39


起源

你有理由相信有一些解决方案吗? - shmosel
@shmosel不是特别的。我正在考虑进口的奇怪角落情况,同时处理一些Error Prone检查。 - Andy Turner
我很乐意看到其中一位语言架构师回答这个问题。 - Jacob G.
如果你遵循标准就不会发生这个问题 Java命名约定,即所有小写的包名和混合大小写的类名。命名约定存在是有原因的。 - Andreas
@Andreas我同意;但我问的是什么是允许的 语言,而不是惯例使用的。 - Andy Turner


答案:


你不能再直接参考了 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

3
2018-05-31 23:11



当然,你可以得到一个 实际  java.util.ArrayList 通过使用类似的东西 Stream.of().collect(Collectors.toList())。 - Andy Turner
@AndyTurner听起来你应该回答自己的问题。我查了源代码 Collectors.toList,它使用 ArrayList::new,确认你的主张。 - rgettman
也许。这不是我想问的问题的答案,你可以参考 类型,不是你如何获得该类型的实例。 - Andy Turner


我要走出去,说不可能从内部引用它 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 会很好的。但是,如果你打电话 List<Integer> list = Idetwo.getList(); 内 Ideone的主要方法,你需要导入 java.util.*,因为个别进口不起作用(有趣)。


3
2018-05-31 23:09



很好的尝试,但我说“在同一个班级”,而不是“在同一个 编制单位“;) - Andy Turner
@AndyTurner Darn,错过了;) - Jacob G.


你有没有尝试过

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

自从我考虑过类加载器以来,已经很长时间了,但IIRC .loadClass() 将优先尝试从最基本的类加载器和真实加载 java.util package应该由bootstrap类加载器提供,它提供的优先级高于您在应用程序中定义的任何内容。


2
2018-05-31 23:00



如果你打电话,这应该工作 loadClass 代替 findClass,是的 protected。 - rgettman


这就是为什么包以小写字母开头,以及大写字母的类型,因为 第一版java的官方代码约定。我不认为我见过java代码打破了这个惯例。

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

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

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


1
2018-05-31 23:10



我建议你回复@Andreas的评论:“我问的是语言允许什么,而不是惯例使用的内容”。 - Andy Turner