[英]Instantiating object of type parameter
我有一个模板 class 如下:
class MyClass<T>
{
T field;
public void myMethod()
{
field = new T(); // gives compiler error
}
}
如何在我的 class 中创建 T 的新实例?
在类型擦除之后,关于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();
}
}
另一种非反射方法是使用混合的构建器/抽象工厂模式。
在 Effective Java 中,Joshua Bloch 详细介绍了 Builder 模式,并提倡通用的 Builder 接口:
public interface Builder<T> {
public T build();
}
具体的builder可以实现这个接口,外部类可以根据需要使用concretebuilder配置Builder。 构建器可以作为Builder<T>
传递给 MyClass。
使用此模式,您可以获得T
的新实例,即使T
具有构造函数参数或需要额外配置。 当然,您需要一些方法将 Builder 传递给 MyClass。 如果你不能将任何东西传递给 MyClass,那么 Builder 和 Abstract Factory 就不行了。
这可能比您正在寻找的更重量级,但它也可以工作。 请注意,如果您采用这种方法,则在构造时将工厂注入 MyClass 而不是在每次调用时将其传递给您的方法会更有意义。
interface MyFactory<T>
{
T newObject();
}
class MyClass<T>
{
T field;
public void myMethod(MyFactory<T> factory)
{
field = factory.newObject()
}
}
如果您愿意子类化,您也可以避免擦除,请查看http://www.artima.com/weblogs/viewpost.jsp?thread=208860
您可以在不传递任何参数的情况下获取类型T
的类对象。
尝试这个。
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();
如果T
的值是 class 或接口定义的一部分,则可以使用TypeTools恢复它。 前任:
class MyStringClass extends MyClass<String> {}
Class<?> typeArg = TypeResolver.resolveRawArgument(MyClass.class, MyStringClass.class);
assert typeArg == String.class;
然后,您可以像往常一样使用反射实例化 typeArg。 如果T
的值不是类型定义的一部分,那么它会从字节码中删除并且无法恢复。
一种选择是使用 Object 进行投射
{field = (T) new Object();}
字段最初将是 Object 类型,但随后将转换为 T 类型。这是一个丑陋的,因为将转换为零是对象初始化的目标。 但我认为这会奏效。
类 classOfT
try {
t = classOfT.newInstance();//new T(); NOTE: type parameter T cannot be instantiated directly
} catch (Exception e) {
e.printStackTrace();
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.