有可能在Java中创建泛型类型的实例吗?我在想,根据我所看到的,答案是否定的(由于类型擦除),但如果有人能看到我遗漏的东西,我会很感兴趣:
class SomeContainer<E>
{
E createContents()
{
return what???
}
}
编辑:事实证明,超级类型令牌可以用来解决我的问题,但它需要大量基于反射的代码,如下面的一些答案所示。
我将把这个问题放一段时间,看看是否有人提出了与Ian Robertson的Artima文章截然不同的东西。
下面是基于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的客户端在创建实例时总是使用{},方法是让这个类成为抽象类:我没有这样做,只是为了显示被擦除的测试用例。