12

Assuming a generic type declaration (Java)

class Foo<T> {
    public T bar;
}

how can I, at runtime, instantiate a Type object that represents Foo parameterized over a specific type T (also known only at runtime)?

SoftMemes
  • 5,602
  • 4
  • 32
  • 61
  • 1
    Why do you need it? Type erasure makes the idea pointless, so try to explain what you are trying to achieve. – Viruzzo Feb 02 '12 at 12:00
  • I need it in order to give a (de)serialization library (google gson in this case) a type object representing my expected types when deserializing from a JSON string. Erasure doesn't make it pointless as the type object will be inspected at runtime. – SoftMemes Feb 02 '12 at 12:07
  • "What is needed is for the library I'm using to see the field as being of the type provided at runtime", "a type object representing my expected types" what does that mean? Can you elaborate on what you are trying to do? – Viruzzo Feb 02 '12 at 13:19
  • @Viruzzo, I need to tell a library using reflection (gson in this case), that I want it to instantiate a populate an object of a specific type from a JSON string. Gson will use reflection to retreive the type of the field bar, I want to control what that type will be seen as in order to direct the deserialization. – SoftMemes Feb 02 '12 at 13:30

6 Answers6

16

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)

newacct
  • 119,665
  • 29
  • 163
  • 224
  • 1
    Most awesome, you appear to be the only one to get it, and you have a concrete solution. I'm surprised that there is no "official" way of doing this, but this method should definitely be enough to get it to play nice with gson, which is all I need. Thank you! – SoftMemes Feb 03 '12 at 11:00
  • Well, there is a simple reason for that... this does not what you asked for. It is an hint for Gson how to parse, not a method to create the type. – Hauke Ingmar Schmidt Feb 03 '12 at 15:13
  • I am looking for this for use when generating spring beans, but i need to build a Class> instance => MyGenericType.class Spring will only take a Class>, is it possible to have it include a specific generic type with bound parameter? – MHGameWork Jun 15 '16 at 17:12
  • I also found one implementation in each of Guava, Apache Commons-Lang, Spring-Core, Hibernate Validator (and actually two in the Sun APIs). – Paŭlo Ebermann May 27 '18 at 21:18
5

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

AlexV
  • 3,836
  • 7
  • 31
  • 37
  • The only solution that worked for me, instead of `String.class` I have my `clazz` param of which is a final param of type `Class`. – yonilevy Aug 10 '14 at 12:21
2

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.

Bohemian
  • 412,405
  • 93
  • 575
  • 722
  • Though I don't need simply to instantiate a type that is determined at runtime, I need to create a Type object that retains the type information. As Jon pointed out, type information isn't always lost. One potential way would be to create a new type dynamically subclassing my generic type with its parameters provided? I don't actually need it to represent Foo, as long as it's assignable to Foo... – SoftMemes Feb 02 '12 at 12:42
  • As for your gson example, that is my workaround too, but it would be nicer if it could be avoided. My case is slightly different though, in that I do know the expected type before deserializing, but I'm building a generic base class to deal with messages sharing some overall structure but differing in the layout of the payload ... – SoftMemes Feb 02 '12 at 12:46
-1

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.

Viruzzo
  • 3,025
  • 13
  • 13
  • I have sen this, but it still assumes that you know the type at compile time, which is not the case for me. In order to instantiate a "TypeToken", I need to instantiate an open generic type with a type parameter at runtime, and how to do this is my original question ... – SoftMemes Feb 02 '12 at 15:00
  • For that you need Bohemian's solution of using a `Class` class member that is initialized in the constructor (or an equivalent `String`). There is no clean way around erasure, all the other ones I've seen are far more fidgety. – Viruzzo Feb 02 '12 at 15:05
  • So, I do have a Class object representing the actual type of T, but that still isn't enough to tell gson that I expect a named attribute to be of this type. – SoftMemes Feb 02 '12 at 19:18
  • Assuming the doc's example above, you would do something like this `Type fooType = new TypeToken>() {}.getType(); gson.fromJson(json, fooType);` when deserializing, where `className` is a String with the type name. – Viruzzo Feb 03 '12 at 07:45
  • That would not work... I do have the Class instance, that is not my problem. – SoftMemes Feb 03 '12 at 10:45
  • What I don't really understand is why you want the library to be able to tell: even without serializing to JSON, the type would normally be erased (barring a few cases like that from Jon Skeet). Usually when a library requires type information it allows you to specify a Class object (for example when fetching from JDO). – Viruzzo Feb 03 '12 at 11:00
  • Viruzzo, the library does indeed allow me to specify a Class object (or a Type), but as the generic part is "inside" the class I need to deserialize, I cannot simply pass the Class instance of the type parameter. Telling Gson about the full type is absolutely required to have it deserialize correctly. – SoftMemes Feb 04 '12 at 11:24
-1

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.

PaulMurrayCbr
  • 1,167
  • 12
  • 16
  • No, this does not answer my question at all. I _do_ have a Class object for the type argument, this is not the matter of getting the Class of a generic type argument at runtime, it's a matter of instantiating a generic Type instance at runtime. – SoftMemes Feb 03 '12 at 10:44
-2

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.

Roman Minenok
  • 9,328
  • 4
  • 26
  • 26
  • 1
    -1 if I could; Freed explicitly said that T is only known at runtime. Therefore, this solution doesn't work for him, as newacct also explained in his answer. – Michel Jung May 22 '13 at 11:44