简体   繁体   中英

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?

User supports userType and userFunction by config:

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

we expect to get data for user and feed data to 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;
}

How can I implement this function without error? If it is not possible, Is there another way?

At last, I use Object to stand all type names. But user must transform Object type to userType manually. Is there a better way?

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

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

You can use the Reflection API to get the instance of Classes, Methods and Fields;

First you can get the Class using

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

You can get the methods from that class using

 c.getDeclaredMethod("Method Name");

and same goes for the Fields

 c.getDeclaredField("Field Name");

Ah, you should know that generics in java are only syntax sugar in compiler for most of the time, so if you don't know runtime type just use wildcard or raw type:
MyClass<?> myClass = new MyClass<>(); , MyClass myClass = new MyClass();
But if user input is always of known type like that String then you can just do:
MyClass<String> myClass = new MyClass<>();
You can also create generic constructor in your class like this:

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

So generic type will be of type of data you passed to it - but this only exist at compile time, in runtime java IGNORES all generics here, like this is valid java code:

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

Java only saves generic type of fields, super classes ( class MyMap extends HashMap<String, Integer> - you can get that super type with generic parameters) and methods. But they are not used in runtime and but reflections can be used to read that type.
For local variables this is not possible and would be useless due to that type erasure.

There is also common pattern for unsafe generic cast, but you should be very careful when to use it:

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

and then this can be used like this:
ArrayList<Whatever> list = doSomething("java.util.ArrayList");
but it can be also used like this:
Integer list = doSomething("java.util.ArrayList");
And this will throw exception at runtime, and this is why it is very unsafe.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM