tags:

views:

19095

answers:

9

Is it possible to create an instance of a generic type in Java? I'm thinking based on what I've seen that the answer is "no" (due to type erasure), but I'd be interested if anyone can see something I'm missing:

class SomeContainer<E>
{
    E createContents()
    {
        return what???
    }
}

EDIT: It turns out that Super Type Tokens could be used to resolve my issue, but it requires a lot of reflection-based code, as some of the answers below have indicated.

I'll leave this open for a little while to see if anyone comes up with anything dramatically different than Ian Robertson's Artima Article.

A: 

As you said, you can't really do it because of type erasure. You can sort of do it using reflection, but it requires a lot of code and lot of error handling.

Adam Rosenfield
How would you even do it using reflection? The only method I see is Class.getTypeParameters(), but that only returns the declared types, not the run-time types.
David Citron
Are you talking about this? http://www.artima.com/weblogs/viewpost.jsp?thread=208860
David Citron
+7  A: 

You are correct. You can't do "new E". But you can change it to

private static class SomeContainer<E>
{
    E createContents(Class<E> clazz)
    {
        return clazz.newInstance();
    }
}

It's a pain. But it works. Wrapping it in the factory pattern makes it a little more tolerable.

Justin Rudd
Yeah, I saw that solution, but it only works if you already have a reference to a Class object of the type that you want to instantiate.
David Citron
Yeah I know. It would be nice if you could do E.class but that simply gives you Object.class because of erasure :)
Justin Rudd
That's the correct approach for this problem. It's usually not what you wish, but it's what you get.
Joachim Sauer
A: 

Here is an option I came up with, it may help:

public static class Container<E> {
    private Class<E> clazz;

    public Container(Class<E> clazz) {
        this.clazz = clazz;
    }

    public E createContents() throws Exception {
        return clazz.newInstance();
    }
}

EDIT: Alternatively you can use this constructor (but it requires an instance of E):

@SuppressWarnings("unchecked")
public Container(E instance) {
    this.clazz = (Class<E>) instance.getClass();
}
Mike Stone
Yeah, this works the same even without generics--with generics the instantiation of this container becomes a bit redundant (you have to specify what "E" is twice).
David Citron
well, that's what happens when you use Java and generics... they aren't pretty, and there are severe limitations...
Mike Stone
+1  A: 

Dunno if this helps, but when you subclass (including anonymously) a generic type, the type information is available via reflection. e.g.,

public abstract class Foo<E> {

  public E instance;  

  public Foo() throws Exception {
    instance = ((Class)((ParameterizedType)this.getClass().
       getGenericSuperclass()).getActualTypeArguments()[0]).newInstance();
    ...
  }

}

So, when you subclass Foo, you get an instance of Bar e.g.,

// notice that this in anonymous subclass of Foo
assert( new Foo<Bar>() {}.instance instanceof Bar );

But it's a lot of work, and only works for subclasses. Can be handy though.

noah
+7  A: 

You'll need some kind of abstract factory of one sort or another to pass the buck to:

interface Factory<E> {
    E create();
}

class SomeContainer<E> {
    private final Factory<E> factory;
    SomeContainer(Factory<E> factory) {
        this.factory = factory;
    }
    E createContents() {
        return factory.create();
    }
}
Tom Hawtin - tackline
A: 

If you mean new E() then it is impossible. And I would add that it is not always correct - how do you know if E has public no-args constructor? But you can always delegate creation to some other class that knows how to create an instance - it can be Class<E> or your custom code like this

interface Factory<E>{
    E create();
}    

class IntegerFactory implements Factory<Integer>{    
  private static int i = 0; 
  Integer create() {        
    return i++;    
  }
}
Pavel Feldman
A: 

If you want not to type class name twice during instantiation like in:

new SomeContainer<SomeType>(SomeType.class);

You can use factory method:

<E> SomeContainer<E> createContainer(Class<E> class);

Like in:

public class Container<E> {

    public static <E> Container<E> create(Class<E> c) {
     return new Container<E>(c);
    }

    Class<E> c;

    public Container(Class<E> c) {
     super();
     this.c = c;
    }

    public E createInstance()
      throws InstantiationException,
      IllegalAccessException {
     return c.newInstance();
    }

}
jb
A: 

You can use Class.forName(String).getConstructor(arguments types).newInstance(arguments), but you need to supply the exact class name, including packages, eg. "java.io.FileInputStream". I use this to create math expressions parser.

And how do you get the exact class name of the generic type at runtime?
David Citron
A: 
package org.foo.com;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

/**
 * Basically the same answer as noah's.
 */
public class Home<E>
{

    @SuppressWarnings ("unchecked")
    public Class<E> getTypeParameterClass()
    {
        Type type = getClass().getGenericSuperclass();
        ParameterizedType paramType = (ParameterizedType) type;
        return (Class<E>) paramType.getActualTypeArguments()[0];
    }

    private static class StringHome extends Home<String>
    {
    }

    private static class StringBuilderHome extends Home<StringBuilder>
    {
    }

    private static class StringBufferHome extends Home<StringBuffer>
    {
    }   

    /**
     * This prints "String", "StringBuilder" and "StringBuffer"
     */
    public static void main(String[] args) throws InstantiationException, IllegalAccessException
    {
        Object object0 = new StringHome().getTypeParameterClass().newInstance();
        Object object1 = new StringBuilderHome().getTypeParameterClass().newInstance();
        Object object2 = new StringBufferHome().getTypeParameterClass().newInstance();
        System.out.println(object0.getClass().getSimpleName());
        System.out.println(object1.getClass().getSimpleName());
        System.out.println(object2.getClass().getSimpleName());
    }

}
Lars Bohl
Good approach by this code may cause ClassCastException if You use in the generic a generic type. Then You retrive the actualType argument You should check that it is also ParamterizedType and if so return his RawType (or something better than this). Another issue with this is when we extend more then once this code also will throw the ClassCastExeption.
Vash