有可能在Java中创建泛型类型的实例吗?我在想,根据我所看到的,答案是否定的(由于类型擦除),但如果有人能看到我遗漏的东西,我会很感兴趣:

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

编辑:事实证明,超级类型令牌可以用来解决我的问题,但它需要大量基于反射的代码,如下面的一些答案所示。

我将把这个问题放一段时间,看看是否有人提出了与Ian Robertson的Artima文章截然不同的东西。


当前回答

在Java 8中,你可以使用Supplier函数接口很容易地实现这一点:

class SomeContainer<E> {
  private Supplier<E> supplier;

  SomeContainer(Supplier<E> supplier) {
    this.supplier = supplier;
  }

  E createContents() {
    return supplier.get();
  }
}

你可以这样构造这个类:

SomeContainer<String> stringContainer = new SomeContainer<>(String::new);

这一行上的String::new语法是一个构造函数引用。

如果你的构造函数接受参数,你可以使用lambda表达式:

SomeContainer<BigInteger> bigIntegerContainer
    = new SomeContainer<>(() -> new BigInteger(1));

其他回答

我想我可以这样做,但很失望:它不起作用,但我认为它仍然值得分享。

也许有人可以纠正:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

interface SomeContainer<E> {
    E createContents();
}

public class Main {

    @SuppressWarnings("unchecked")
    public static <E> SomeContainer<E> createSomeContainer() {
        return (SomeContainer<E>) Proxy.newProxyInstance(Main.class.getClassLoader(),
                new Class[]{ SomeContainer.class }, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Class<?> returnType = method.getReturnType();
                return returnType.newInstance();
            }
        });
    }

    public static void main(String[] args) {
        SomeContainer<String> container = createSomeContainer();

    [*] System.out.println("String created: [" +container.createContents()+"]");

    }
}

它产生:

Exception in thread "main" java.lang.ClassCastException: java.lang.Object cannot be cast to java.lang.String
    at Main.main(Main.java:26)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:601)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)

第26行带有[*]。

唯一可行的解决方案是@JustinRudd的方案

下面是一个createContents的实现,它使用TypeTools(我编写的)来解析由E表示的原始类:

E createContents() throws Exception {
  return TypeTools.resolveRawArgument(SomeContainer.class, getClass()).newInstance();
}

这种方法只适用于SomeContainer被子类化,所以E的实际值在类型定义中被捕获:

class SomeStringContainer extends SomeContainer<String>

否则E的值将在运行时被擦除,并且不可恢复。

下面是基于ParameterizedType的改进解决方案。getActualTypeArguments, @noah、@Lars Bohl和其他一些人已经提到过。

首先是实施上的小改进。Factory不应该返回实例,而是返回类型。一旦使用Class.newInstance()返回实例,就减少了使用范围。因为只有无参数构造函数可以像这样调用。更好的方法是返回一个类型,并允许客户端选择他想调用的构造函数:

public class TypeReference<T> {
  public Class<T> type(){
    try {
      ParameterizedType pt = (ParameterizedType) this.getClass().getGenericSuperclass();
      if (pt.getActualTypeArguments() == null || pt.getActualTypeArguments().length == 0){
        throw new IllegalStateException("Could not define type");
      }
      if (pt.getActualTypeArguments().length != 1){
        throw new IllegalStateException("More than one type has been found");
      }
      Type type = pt.getActualTypeArguments()[0];
      String typeAsString = type.getTypeName();
      return (Class<T>) Class.forName(typeAsString);

    } catch (Exception e){
      throw new IllegalStateException("Could not identify type", e);
    }

  }
}

下面是一个用法示例。@Lars Bohl只展示了一种通过扩展具体化通用的方式。@noah只能通过使用{}创建实例。下面是演示这两种情况的测试:

import java.lang.reflect.Constructor;

public class TypeReferenceTest {

  private static final String NAME = "Peter";

  private static class Person{
    final String name;

    Person(String name) {
      this.name = name;
    }
  }

  @Test
  public void erased() {
    TypeReference<Person> p = new TypeReference<>();
    Assert.assertNotNull(p);
    try {
      p.type();
      Assert.fail();
    } catch (Exception e){
      Assert.assertEquals("Could not identify type", e.getMessage());
    }
  }

  @Test
  public void reified() throws Exception {
    TypeReference<Person> p = new TypeReference<Person>(){};
    Assert.assertNotNull(p);
    Assert.assertEquals(Person.class.getName(), p.type().getName());
    Constructor ctor = p.type().getDeclaredConstructor(NAME.getClass());
    Assert.assertNotNull(ctor);
    Person person = (Person) ctor.newInstance(NAME);
    Assert.assertEquals(NAME, person.name);
  }

  static class TypeReferencePerson extends TypeReference<Person>{}

  @Test
  public void reifiedExtenension() throws Exception {
    TypeReference<Person> p = new TypeReferencePerson();
    Assert.assertNotNull(p);
    Assert.assertEquals(Person.class.getName(), p.type().getName());
    Constructor ctor = p.type().getDeclaredConstructor(NAME.getClass());
    Assert.assertNotNull(ctor);
    Person person = (Person) ctor.newInstance(NAME);
    Assert.assertEquals(NAME, person.name);
  }
}

注意:你可以强制TypeReference的客户端在创建实例时总是使用{},方法是让这个类成为抽象类:我没有这样做,只是为了显示被擦除的测试用例。

在Java 8中,你可以使用Supplier函数接口很容易地实现这一点:

class SomeContainer<E> {
  private Supplier<E> supplier;

  SomeContainer(Supplier<E> supplier) {
    this.supplier = supplier;
  }

  E createContents() {
    return supplier.get();
  }
}

你可以这样构造这个类:

SomeContainer<String> stringContainer = new SomeContainer<>(String::new);

这一行上的String::new语法是一个构造函数引用。

如果你的构造函数接受参数,你可以使用lambda表达式:

SomeContainer<BigInteger> bigIntegerContainer
    = new SomeContainer<>(() -> new BigInteger(1));

我不知道这是否有帮助,但当您子类化(包括匿名)泛型类型时,类型信息可以通过反射获得。例如,

public abstract class Foo<E> {

  public E instance;  

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

}

所以,当你子类化Foo时,你得到Bar的一个实例,例如,

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

但是工作量很大,而且只适用于子类。不过也很方便。