实例化类型参数的对象

我有一个如下的模板类:

class MyClass<T>
{
T field;
public void myMethod()
{
field = new T(); // gives compiler error
}
}

如何在类中创建 T 的新实例?

86189 次浏览

在类型消除之后,所有关于 T的知识就是它是 Object的某个子类。您需要指定一些工厂来创建 T的实例。

一种方法可以使用 Supplier<T>:

class MyClass<T> {


private final Supplier<? extends T> ctor;


private T field;


MyClass(Supplier<? extends T> ctor) {
this.ctor = Objects.requireNonNull(ctor);
}


public void myMethod() {
field = ctor.get();
}


}

用法可能是这样的:

MyClass<StringBuilder> it = new MyClass<>(StringBuilder::new);

或者,您可以提供一个 Class<T>对象,然后使用反射。

class MyClass<T> {


private final Constructor<? extends T> ctor;


private T field;


MyClass(Class<? extends T> impl) throws NoSuchMethodException {
this.ctor = impl.getConstructor();
}


public void myMethod() throws Exception {
field = ctor.newInstance();
}


}

如果你愿意子类化,你也可以避免擦除,检查一下 Http://www.artima.com/weblogs/viewpost.jsp?thread=208860

这可能比你所寻找的更重量级,但它也将工作。请注意,如果采用这种方法,那么在构造工厂时将其注入 MyClass 将更有意义,而不是在每次调用它时将其传递给方法。

interface MyFactory<T>
{
T newObject();
}


class MyClass<T>
{
T field;
public void myMethod(MyFactory<T> factory)
{
field = factory.newObject()
}
}

另一种非反射方法是使用混合的 Builder/抽象工厂模式。

In Effective Java, Joshua Bloch goes over the Builder pattern in detail, and advocates a generic Builder interface:

public interface Builder<T> {
public T build();
}

具体生成器可以实现此接口,外部类可以使用具体生成器根据需要配置生成器。构建器可以作为 Builder<T>传递给 MyClass。

使用此模式,即使 T具有构造函数参数或需要额外的配置,您也可以获得 T的新实例。当然,您需要一些方法来将 Builder 传递到 MyClass。如果您不能向 MyClass 传递任何内容,那么 Builder 和抽象工厂将被淘汰。

Class classOfT

        try {
t = classOfT.newInstance();//new T(); NOTE: type parameter T cannot be instantiated directly
} catch (Exception e) {
e.printStackTrace();
}

一种选择是使用 Object 强制转换它

{field = (T) new Object();}

字段最初将类型为 Object,但随后将强制转换为 T。这是一个丑陋的问题,因为将强制转换减少到零是对象初始化的目标。不过我觉得这样能行。

您可以在不传递任何参数的情况下获取 T类型的类对象。

Try this.

static class MyClass<T> {
Class<?> clazz;


@SafeVarargs
public MyClass(T... dummy) {
if (dummy.length > 0)
throw new IllegalArgumentException("Do not specify arguments");
clazz = dummy.getClass().componentType();
}


@Override
public String toString() {
return "MyClass<T = " + clazz.getName() + ">";
}
}


public static void main(String[] args) {
MyClass<String> s = new MyClass<>();
System.out.println(s);
Object i = new MyClass<Integer>();
System.out.println(i);
}

产出:

MyClass<T = java.lang.String>
MyClass<T = java.lang.Integer>

可以像下面这样实例化类型参数 T的对象。

clazz.getConstructor().newInstance();