簡體   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