有可能在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));

其他回答

当您在编译时使用E时,您并不真正关心实际的泛型类型“E”(无论是使用反射还是使用泛型类型的基类),因此让子类提供E的实例。

abstract class SomeContainer<E>
{
    abstract protected E createContents();
    public void doWork(){
        E obj = createContents();
        // Do the work with E 
     }
}

class BlackContainer extends SomeContainer<Black>{
    protected Black createContents() {
        return new Black();
    }
}

希望这还不算太晚!!

Java是类型安全的,这意味着只有对象才能创建实例。

在我的例子中,我不能将参数传递给createContents方法。我的解决方案是使用与下面的答案不同的扩展。

private static class SomeContainer<E extends Object> {
    E e;
    E createContents() throws Exception{
        return (E) e.getClass().getDeclaredConstructor().newInstance();
    }
}

这是我不能传递参数的例子。

public class SomeContainer<E extends Object> {
    E object;

    void resetObject throws Exception{
        object = (E) object.getClass().getDeclaredConstructor().newInstance();
    }
}

如果使用非对象类型扩展泛型类,则使用反射会产生运行时错误。将泛型类型扩展为对象,将此错误转换为编译时错误。

你说得对。你不能用new E()。但是你可以把它改成

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

这是一种痛苦。但它确实有效。将其包装在工厂模式中使其更易于忍受。

现在就可以这样做,而且不需要一堆反射代码。

import com.google.common.reflect.TypeToken;

public class Q26289147
{
    public static void main(final String[] args) throws IllegalAccessException, InstantiationException
    {
        final StrawManParameterizedClass<String> smpc = new StrawManParameterizedClass<String>() {};
        final String string = (String) smpc.type.getRawType().newInstance();
        System.out.format("string = \"%s\"",string);
    }

    static abstract class StrawManParameterizedClass<T>
    {
        final TypeToken<T> type = new TypeToken<T>(getClass()) {};
    }
}

当然,如果您需要调用需要一些反射的构造函数,但这是很好的文档,这个技巧不是!

下面是用于TypeToken的JavaDoc。

注意,kotlin中的泛型类型可能没有默认构造函数。

 implementation("org.objenesis","objenesis", "3.2")

    val fooType = Foo::class.java
    var instance: T = try {
        fooType.newInstance()
    } catch (e: InstantiationException) {
//            Use Objenesis because the fooType class has not a default constructor
        val objenesis: Objenesis = ObjenesisStd()
        objenesis.newInstance(fooType)
    }

没有默认构造函数 Objenesis