How do I build a Java type object at runtime from a generic type definition and runtime type parameters?

前端 未结 6 1151
无人及你
无人及你 2020-12-05 12:16

Assuming a generic type declaration (Java)

class Foo {
    public T bar;
}

how can I, at runtime, instantiate a Type object that r

相关标签:
6条回答
  • 2020-12-05 12:28

    What everyone else said :) . The class that you want to instantiate needs to be available at runtime. Many ways of doing this: put the class or class name in a variable local to your factory, have a protected method, create an "object factory" class if you need to do this in many different places, etc. This is the kind of job that bean frameworks do, so if you are using one, it might be possible to do it by configuring that.

    0 讨论(0)
  • 2020-12-05 12:31

    Let's say we're talking about List<String> for example.
    You can construct something like this:

        Type type = new ParameterizedType() {
                public Type getRawType() {
                    return List.class;
                }
    
                public Type getOwnerType() {
                    return null;
                }
    
                public Type[] getActualTypeArguments() {
                    return new Type[] { String.class};
                }
            };
    

    If you need this for de-serializing from JSON, you can use this Type from calling gson.fromJson

    0 讨论(0)
  • 2020-12-05 12:31

    It appeared to be really simpler than most people thought:

    Type collectionType = new TypeToken<ArrayList<Person>>(){}.getType();
    ArrayList<Person> persons = gson.fromJson(response, collectionType);
    

    No need to copy ParameterizedTypeImpl class as newacct suggested.

    0 讨论(0)
  • 2020-12-05 12:39

    Gson does actually provide a solution for this: https://sites.google.com/site/gson/gson-user-guide#TOC-Serializing-and-Deserializing-Gener

    Of course what this does is little more than Bohemian's solution (you still need to pass the type parameter somehow), but done automatically for you.

    0 讨论(0)
  • 2020-12-05 12:42

    I think I understand your question. You want to serialize a Foo<T>, and you have the class object of T at runtime (but it's not fixed at compile time). Therefore, the suggested solution in Gson of creating an anonymous subclass of TypeToken does not work because that requires that the parameterized type (e.g. Foo<String>) be hard-coded at compile time, and it does not work if you use something like Foo<T>.

    However, let's look at what the TypeToken method on the Gson site actually accomplishes. You create an object of an anonymous subclass of TypeToken, and then ask for its type parameter using its getType() method. A class's superclass is part of its metadata, and includes the generic parameters of its superclass. So at runtime, it can look at its own inheritance hierarchy, and figure out what type parameter you used for TypeToken, and then returns a java.lang.reflect.Type instance for that type (which, if it is parameterized, will be a ParameterizedType instance). Once you get this Type instance, you are supposed to pass it as the second argument of the toGson().

    All we need to do is find another way to create this instance of ParameterizedType. ParameterizedType is an interface, but unfortunately the public Java API does not provide any concrete implementations or any way to create a ParameterizedType dynamically. There appears to be a class called ParameterizedTypeImpl, in the private Sun APIs and in the Gson code that you can use (e.g. here). You can simply copy the code and rename it into your own class. Then, to create a Type object representing Foo<String> at runtime, you can just do something like new ParameterizedTypeImpl(Foo.class, new Type[]{String.class}, null) (untested)

    0 讨论(0)
  • 2020-12-05 12:43

    The usual way around type erasure is:

    class Foo<T> {
    
        Class<T> clazz;
    
        public Foo(Class<T> c) {
            clazz = c;
        }
    
        public T bar {
            return clazz.newInstance();
        }
    }
    

    If there's no no-args constructor for your T, you can do something fancier using reflection on the Class object; once you have an instance of Class<T>, you can get an instance.

    I faced exactly this problem too with gson. I ended up with this:

    public class JsonWrapper {
        private String className;
        private String json; // the output of the gson.toJson() method
    }
    

    And when I needed to deserialise, I did a Class.forName(className) then I had all I needed to call the fromJson() method of the gson library.

    I couldn't believe gson did not support this natively - it seems like such an obvious thing to want to do... get some json and turn that into an object without knowing which class it is beforehand.

    0 讨论(0)
提交回复
热议问题