关于java:如何使用同名字符串对类进行引用

How to make a reference to a class using a string of the same name

假设我有两个类 Foo 和 Bar,它们实现了接口 A。我要么想制作一个 Foo 或 Bar 的 ArrayList。在命令行参数中,用户必须输入 Foo 或 Bar 中的任意一个,以了解他们正在尝试创建 ArrayList of.

的对象

所以:

1
2
3
4
public static void main(String [] args){
    String type = args[0];
    ArrayList<type> list = new ArrayList<type>();
    }

我如何从字符串类型到对类 Foo 或 Bar 的引用,所以它正在执行

1
2
3
ArrayList<Foo> list = new ArrayList<Foo>();
or
ArrayList<Bar> list = new ArrayList<Bar>();

这样我可以做到以下几点:

1
2
3
4
for(int i = 0 ; i < list.size() ; i++){
    list(i).doSomething();
    //doSomething is a function in interface A that Foo and Bar are required to implement
}

明确地说,我不想要

1
if (type.equals("Foo")) list = ArrayList<Foo>();

我希望能够使用与 type 同名的任何有效类创建一个 ArrayList。


这在 Java 中是不可能的。您可以使用反射检索类型名称,但无法动态创建泛型类型。

原因是泛型类型仅在编译时存在,编译器使用它来强制执行静态类型安全。在运行时,通用信息被丢弃,因此执行您所要求的操作甚至没有意义(在当前的 Java 哲学中)。在运行时,泛型类型已转换为 Object,编译器在必要时替换了类型安全的强制转换。


编译器需要知道类型,听起来您想将类型视为运行时变量而不是编译时事实。

查看在运行时获取类的泛型类型。它本质上问同样的事情,您将获得有关使用反射的可能解决方案的良好信息。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class ForNameTest {
public static void main(String[] argv) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
    java.util.ArrayList<String> arrayList = null;
    try {
        Class< ? > createdClass = Class.forName("java.util.ArrayList<java.lang.String>");
        arrayList = (java.util.ArrayList<String>) (createdClass.newInstance());
        arrayList.add("ABC");
        System.out.println("First case" + arrayList.toString());
    }
    catch (ClassNotFoundException e) {
        System.out.println("Exception in first case:" + e.toString());
    }
    try {
        Class< ? > createdClass = Class.forName("java.util.ArrayList");
        arrayList = (java.util.ArrayList<String>) (createdClass.newInstance());
        arrayList.add("ABC");
        System.out.println("Second case" + arrayList.toString());
    }
    catch (ClassNotFoundException e) {
        System.out.println("Exception in second case:" + e.toString());
    }
}
}

输出:

1
2
3
4
5
6
7
8
C:\\JavaTools>javac ForNameTest.java
Note: ForNameTest.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.

C:\\JavaTools>java ForNameTest
Exception in first case: java.lang.ClassNotFoundException: java.util.ArrayList<java.lang.S
tring>
Second case [ABC]

(不确定这证明了什么——你仍然不能将你的 ArrayList 声明为变量(vs <?>)类型。但就是这样,FWIW。)


您可以维护实现该接口的所有可能类型的列表,然后迭代它们的类型,直到找到与输入字符串匹配的类型。除此之外,我认为您在 Java 中会遇到麻烦。


将数组列表声明为 ArrayList<A>。这样它就可以同时存储 Foo 和 Bar 对象,因为它们都实现了接口 A.


使用 Class.forName(String classNameWithPath) 方法 - 它会做你想做的事。

这里有个例子 - "Class.forName()" 和 "Class.forName().newInstance()" 有什么区别?