3

I tried to employ the code given in this answer. I did this:

public class ListClass<T> {
  private class ObjectFactory<T> {
    //This should the the class of generic type T which is what I need
    public final Class<T> cls;
    @SuppressWarnings ("unchecked")
    public ObjectFactory()
    {
      //This three lines are from the linked answer - I don't really understand them
      Type type = getClass().getGenericSuperclass();
      ParameterizedType paramType = (ParameterizedType) type; //java.lang.ClassCastException: java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType
      cls = (Class<T>) paramType.getActualTypeArguments()[0]; 
    }
    public T createNew(... some parameters ...) {
      return (T)new Expression(cls, "new", new Object[]{... some parameters ...}).getValue();
    }
  }
  public final ObjectFactory<T> factory = new ObjectFactory<T>();

  public generateItem() {
    //let add() be method that adds item of type T to this list
    this.add(factory.createNew(... some parameters ...));
  }
}

What am I dong wrong?

Community
  • 1
  • 1
Tomáš Zato
  • 50,171
  • 52
  • 268
  • 778
  • How do you instantiate the `ObjectFactory`? Really need [\mcve](http://stackoverflow.com/help/mcve). Also see e.g. [this comment](http://stackoverflow.com/questions/3403909/get-generic-type-of-class-at-runtime?lq=1#comment44403216_3403976). – Radiodef May 01 '15 at 13:58
  • 1
    The linked answer will only work with simplistic type hierarchies. For more complex ones, you could consider using [Guava's TypeTokens](https://code.google.com/p/guava-libraries/wiki/ReflectionExplained). – Mick Mnemonic May 01 '15 at 14:01
  • 1
    Why is the constructor of `GameObjectFactory` inside `ObjectFactory` class? – Bubletan May 01 '15 at 14:08
  • @Bubletan The example must be abridged. That's why I asked for an MCVE. – Radiodef May 01 '15 at 14:09
  • @Radiodef I fixed the code. Both constructor and instantiation. – Tomáš Zato May 01 '15 at 14:30

2 Answers2

3

This technique only works if the class is parameterized with a concrete argument like this:

class Sub extends Super<String> {}

And then by extension:

new Super<String>() {}

But not this way:

class Sub<T> extends Super<T> {}

And not this way:

<T> Super<T> m() {
    return new Super<T>() {};
}

The ability to use getActualTypeArguments is a property of the class, not an instance, so the class needs actual type arguments to use it.


Per your edit, what I'd recommend doing is creating a helper routine for this:

static <T> Class<T> getActualTypeArgument(Class<?> clazz, int i) {
    Type superclazz = getClass().getGenericSuperclass();
    ParameterizedType paramType = (ParameterizedType) superclazz;

    @SuppressWarnings("unchecked")
    final Class<T> actual =
        (Class<T>) paramType.getActualTypeArguments()[i];
    return actual;
}

Then use this on the ListClass:

class ListClass<T> {
    private ObjectFactory<T> factory = new ObjectFactory<T>(
        getActualTypeArgument(this.getClass(), 0));
}

And create the ListClass in e.g. the following manner:

ListClass<GameObject> list =
    new ListClass<GameObject>() {};

Consider also just passing the Class<T> in:

class ListClass<T> {
    private ObjectFactory<T> factory;

    ListClass<T>(Class<T> type) {
        factory = new ObjectFactory(type);
    }

    static <T> ListClass<T> of(Class<T> type) {
        return new ListClass<T>(type);
    }
}

ListClass<GameObject> list =
    ListClass.of(GameObject.class);
Radiodef
  • 37,180
  • 14
  • 90
  • 125
2

Quoting the docs on Class#getGenericSuperClass():

Answers the Type for the Class which represents the receiver's superclass. For classes which represent base types, interfaces, and for java.lang.Object the method answers null.

Invoking it on your ObjectFactory<T> class will return the Object type, which is not a ParameterizedType.

Mena
  • 47,782
  • 11
  • 87
  • 106