繁体   English   中英

实例化类型参数的 object

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM