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