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

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

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

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


当前回答

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());
    }

}

其他回答

考虑一种更函数化的方法:与其凭空创建一些E(这显然是一种代码味道),不如传递一个知道如何创建E的函数。

E createContents(Callable<E> makeone) {
     return makeone.call(); // most simple case clearly not that useful
}

摘自Java教程-泛型的限制:

不能创建类型参数的实例

不能创建类型参数的实例。例如,下面的代码会导致编译时错误:

public static <E> void append(List<E> list) {
    E elem = new E();  // compile-time error
    list.add(elem);
}

作为一种变通方法,你可以通过反射创建一个类型参数的对象:

public static <E> void append(List<E> list, Class<E> cls) throws Exception {
    E elem = cls.getDeclaredConstructor().newInstance();   // OK
    list.add(elem);
}

你可以像下面这样调用append方法:

List<String> ls = new ArrayList<>();
append(ls, String.class);

不幸的是,Java不允许你做你想做的事情。查看官方解决方案:

不能创建类型参数的实例。例如,下面的代码会导致编译时错误:

public static <E> void append(List<E> list) {
    E elem = new E();  // compile-time error
    list.add(elem);
}

作为一种变通方法,你可以通过反射创建一个类型参数的对象:

public static <E> void append(List<E> list, Class<E> cls) throws Exception {
    E elem = cls.newInstance();   // OK
    list.add(elem);
}

你可以像下面这样调用append方法:

List<String> ls = new ArrayList<>();
append(ls, String.class);

正如你说的,你不能真正做到这一点,因为类型擦除。你可以使用反射来实现,但这需要大量的代码和错误处理。

你可以用类加载器和类名,最终一些参数。

final ClassLoader classLoader = ...
final Class<?> aClass = classLoader.loadClass("java.lang.Integer");
final Constructor<?> constructor = aClass.getConstructor(int.class);
final Object o = constructor.newInstance(123);
System.out.println("o = " + o);