简体   繁体   English

实例化类型参数的 object

[英]Instantiating object of type parameter

I have got a template class as follows:我有一个模板 class 如下:

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

How do I create a new instance of T in my class?如何在我的 class 中创建 T 的新实例?

After type erasure, all that is known about T is that it is some subclass of Object .在类型擦除之后,关于T的所有信息就是它是Object的某个子类。 You need to specify some factory to create instances of T .您需要指定一些工厂来创建T的实例。

One approach could use a Supplier<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();
  }

}

Usage might look like this:用法可能如下所示:

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

Alternatively, you can provide a Class<T> object, and then use reflection.或者,您可以提供一个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();
  }

}

Another non-reflective approach is to use a hybrid Builder / Abstract Factory pattern.另一种非反射方法是使用混合的构建器/抽象工厂模式。

In Effective Java, Joshua Bloch goes over the Builder pattern in detail, and advocates a generic Builder interface:在 Effective Java 中,Joshua Bloch 详细介绍了 Builder 模式,并提倡通用的 Builder 接口:

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

Concrete builders can implement this interface, and outside classes can use the concrete builder to configure the Builder as required.具体的builder可以实现这个接口,外部类可以根据需要使用concretebuilder配置Builder。 The builder can be passed to MyClass as a Builder<T> .构建器可以作为Builder<T>传递给 MyClass。

Using this pattern, you can get new instances of T , even if T has constructor parameters or requires additional configuration.使用此模式,您可以获得T的新实例,即使T具有构造函数参数或需要额外配置。 Of course, you'll need some way to pass the Builder into MyClass.当然,您需要一些方法将 Builder 传递给 MyClass。 If you can't pass anything into MyClass, then Builder and Abstract Factory are out.如果你不能将任何东西传递给 MyClass,那么 Builder 和 Abstract Factory 就不行了。

This may be more heavyweight than what you're looking for, but it will also work.这可能比您正在寻找的更重量级,但它也可以工作。 Note that if you take this approach, it would make more sense to inject the factory into MyClass when it is constructed instead of passing it into your method each time it is called.请注意,如果您采用这种方法,则在构造时将工厂注入 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

You can get the class object of type T without passing any arguments.您可以在不传递任何参数的情况下获取类型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);
}

output:输出:

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

You can instantiate an object of type parameter T like this.您可以像这样实例化类型参数T的对象。

clazz.getConstructor().newInstance();

If the value of T is part of a class or interface definition, it can be recovered with TypeTools .如果T的值是 class 或接口定义的一部分,则可以使用TypeTools恢复它。 Ex:前任:

class MyStringClass extends MyClass<String> {}

Class<?> typeArg = TypeResolver.resolveRawArgument(MyClass.class, MyStringClass.class);

assert typeArg == String.class;

Then you can instantiate the typeArg as usual using reflection.然后,您可以像往常一样使用反射实例化 typeArg。 If the value of T is not part of a type definition, then it's erased from the bytecode and won't be recoverable.如果T的值不是类型定义的一部分,那么它会从字节码中删除并且无法恢复。

One option would be to cast it with Object一种选择是使用 Object 进行投射

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

field will initially be type Object, but then it will cast down to type T. This is an ugly one because reducing casting to zero is what the goal should be for object initializations.字段最初将是 Object 类型,但随后将转换为 T 类型。这是一个丑陋的,因为将转换为零是对象初始化的目标。 But I think this will work.但我认为这会奏效。

Class classOfT类 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