给定一个字符串作为泛型 class 的类型名称,我如何在运行时创建实例。如果不可能,还有其他方法吗?

Given a string as the type name of a generic class, how can I create the instance in runtime. If it is not possible, Is there another way?

用户通过配置支持 userType 和 userFunction:

{"userType": "com.user.Person", "userFunction": "com.user.userFunction"}

我们希望获取用户数据并将数据提供给 userFunction:

public class DataConsumer<T> {
    //get data from some place, such as kafka
    T data;
}
process() {
    String userType = userInput();
    DataConsumer<userType> dataConsumer = new DataConsumer<userType>(); // error because generic class's type name don't support String or Class 
    userFunction(dataConsumer.data); // We get this function by reflection
}

// This is ok in the user side
userFunction(userType data) {
    data;
}

如何实现这个功能才不会报错?如果不行,还有别的办法吗?

最后,我用Object来表示所有的类型名。但用户必须手动将 Object 类型转换为 userType。有没有更好的方法?

{
    String userType = userInput();
    DataConsumer<Object> dataConsumer = new DataConsumer<Object>(); // ok
    userFunction(dataConsumer.data); 
}

userFunction(Object data) {
    (userType)data;
}

您可以使用反射 API 获取 Classes、方法和字段的实例;

首先你可以使用

获得Class
 Class c = Class.forName("Class Name");

您可以使用

从 class 中获取方法
 c.getDeclaredMethod("Method Name");

字段也是如此

 c.getDeclaredField("Field Name");

啊,你应该知道 java 中的泛型在大多数情况下只是编译器中的语法糖,所以如果你不知道运行时类型,请使用通配符或原始类型:
MyClass<?> myClass = new MyClass<>();MyClass myClass = new MyClass();
但是如果用户输入总是像 String 这样的已知类型,那么你可以这样做:
MyClass<String> myClass = new MyClass<>();
您还可以像这样在 class 中创建通用构造函数:

public class MyClass<T> {
    T data;
    public <T> MyClass(T data) {this.data = data;}
}

所以泛型类型将是您传递给它的数据类型 - 但这只存在于编译时,在运行时 java 忽略这里的所有泛型,像这样是有效的 java 代码:

List<String> strings = new ArrayList<>();
strings.add(“a”);
List<Integer> integersKindOf = (List)strings;
integersKindOf.remove(“a”);

Java 只保存通用类型的字段,超级 classes(class MyMap extends HashMap<String, Integer> - 你可以得到带有通用参数的超级类型)和方法。但它们不在运行时使用,但可以使用反射来读取该类型。
对于局部变量,这是不可能的,并且由于该类型擦除而无用。

unsafe generic cast 也有通用模式,但使用时要格外小心:

public <T> T doSomething(String type) {
    return (T) Class.forName(type).newInstance();
}

然后可以这样使用:
ArrayList<Whatever> list = doSomething("java.util.ArrayList");
但也可以这样使用:
Integer list = doSomething("java.util.ArrayList");
而且这会在运行时抛出异常,这也是它非常不安全的原因。