[英]How do I instantiate an instance of a generic method parameter in Java?
請考慮以下代碼:
// ...
public class BaseClass
{
public BaseClass (int theParam)
{
// ...whatever...
}
}
public class DerivedType
{
// ...Content does not matter...
}
// ...elsewhere:
public <ElemType extends BaseClass> boolean doIt (ArrayList<ElemType> target)
{
ElemType newElem=new ElemType (5) ; // "Cannot instantiate this type"
// ...other code does not matter...
return true ;
}
// ..
如何在doIt
創建ElemType
類型的實例?
顯示的構造產生指示的誤差。
ElemType.newInstance
不存在,這讓我感到驚訝。
我幾乎閱讀了所有常見問題解答,答案和googleable資料,但我找不到任何有用的信息。
編輯:是的我知道反思有它的缺點,並且由於種種原因不是最終的解決方案。 問題不是“我應該這樣做”,而是“我該怎么辦”。
請考慮編譯器在編譯時擦除泛型信息並將其替換為object。 內部泛型只是從java.lang.Object轉換為java.lang.Object。
這也是為什么在運行時很難獲得通用信息的原因,即使它是可能的。
看到這里: 谷歌 。
在一個詳細說明:如果你需要做這樣的事情,通常是糟糕的設計。 我在這種情況下有幾次,但我每次都找到了更好的解決方案:)。 所以,只要考慮一下你的代碼中是否真的需要這么臟的黑客攻擊。
編輯:關於評論部分,需要更詳細的解釋。
無論如何,應該謹慎使用反射,因為從軟件工程的角度來看,它被認為是糟糕的設計。 為什么? 它可能會引入一些難以發現的錯誤,因為反射會改變應用程序的自然流程並使用在開發時並不總是可見的信息。 這突然出現了意想不到的行為。
即使我沒有這方面的正式證據,但我說每次你需要反思時,你的問題還有另一個解決方案(如果生成軟件開發是一個選項;))。
所以最后,在99%的情況下,反射只不過是一個懶惰程序員的骯臟黑客。 這可能與100%的所有程序員都很懶惰這一事實有關,但無論如何。
編輯2:
既然你想要代碼:
abstract class Foo<T> {
private Class<T> tClass;
T field;
public void bar(Class<T> clazz) {
Type type = getClass().getGenericSuperclass();
if (type instanceof ParameterizedType) {
ParameterizedType paramType = (ParameterizedType)type;
tClass = (Class<T>) paramType.getActualTypeArguments()[0];
field = tClass.newInstance();
}
}
}
(摘自: 這里 )
如上所述,泛型類型的類型擦除不允許這樣做。 但是你可以達到你想要的效果:
public class BaseClass {
public BaseClass(int theParam) {
// ...whatever...
}
public BaseClass() {
}
}
public class DerivedType extends BaseClass {
}
現在doIt()方法獲取引用的類參數:
public <D extends BaseClass> boolean doIt (ArrayList<D> target, Class<D> c)
{
try {
D newElem = c.getDeclaredConstructor(int.class).newInstance(5);
} catch (Exception e) {}
// ...other code does not matter...
return true ;
}
你應該這樣稱呼它:
ArrayList<DerivedType> testList = new ArrayList<DerivedType>();
testList.add(new DerivedType());
testList.add(new DerivedType());
doIt(testList, DerivedType.class);
希望有幫助:)
注意,一個人可能真的想要hacky並擺脫class參數並試試這個:
public static <D extends BaseClass> boolean doIt (ArrayList<D> target)
{
try {
D newElem1 = ((Class<D>) ((ParameterizedType) target.getClass().getGenericSuperclass()).getActualTypeArguments()[0]).getDeclaredConstructor(int.class).newInstance(5);
} catch (Exception e) { e.printStackTrace();}
return true ;
}
}
事實上我在第二次編輯之前就這么認為:)但是這會得到一個“ java.lang.ClassCastException:sun.reflect.generics.reflectiveObjects.TypeVariableImpl不能被強制轉換為java.lang.Class ”異常,如你所說(我沒有看到它是因為一個被忽視的捕獲聲明)。 簡而言之,Java運行時系統不存儲參數化類型(支持向后兼容性;因此將來可能會更改)。
所以,看起來如果不“觸動”某些課程就不可能。
但是,除了上述方法之外,我還可以考慮另外兩件事 。 首先,如果BaseClass和DerivedType'D'類都實現了clone()方法,您可以從數組中獲取對象的克隆,然后使用它:
D o = target.get(0);
D oNew = (D)((BaseClass)o).clone();
target.add(oNew);
多態性將照顧其余的:)
第二個不是真正的“解決方案”,但如果你想要的只是按類型參數化的對象數組的新實例,則可以使用它。 類型擦除僅發生於參數化類型,但它並不適用於基本陣列發生(數組在JVM 物化 )。 因此,如果我們可以自由更改方法的簽名並使用數組就可以了,那么以下方法就可以了:
public <D extends BaseClass> boolean doIt(D[] target) {
try {
D newD = (D) (target.getClass().getComponentType().getConstructor(int.class).newInstance(8));
target[0] = newD;
// The following is optional, if we want to work with Collections internally
List<D> l = new ArrayList<D>(Arrays.asList(target));
l.add(newD);
} catch (Exception e) {
e.printStackTrace();
}
return true;
}
注意:如果我們不能引入新參數,超級類型令牌將無法解決此問題。 如果我錯了,請糾正我。
您無法在此處創建ElemType對象,因為一旦實例化通用代碼,編譯器就無法准確知道ElemType將是什么。
為了允許創建ElemType,我將提供某種工廠對象。 您可以使用Java反射類,但可能更容易提供您自己的工廠基類或接口。
想象一下你有這個:
public class DerivedType extends BaseClass
{
// No more one argument constructor
public DerivedType() {
super(0);
}
}
然后是電話
DerivedType d = new DerivedType(5);
無效......
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.