如何在Java中实例化generics方法参数的实例?
请考虑以下代码:
// ... public class BaseClass { public BaseClass (int theParam) { // ...whatever... } } public class DerivedType { // ...Content does not matter... } // ...elsewhere: public boolean doIt (ArrayList target) { ElemType newElem=new ElemType (5) ; // "Cannot instantiate this type" // ...other code does not matter... return true ; } // ..
如何在doIt
创建ElemType
类型的实例?
显示的构造产生指示的误差。
ElemType.newInstance
不存在,这让我感到惊讶。
我几乎阅读了所有的常见问题解答,答案和googleable资料,但我找不到任何有用的信息。
编辑:是的我知道反思有其缺点,并不是最终的解决方案,原因很多。 问题不是“我应该这样做”,而是“我该怎么办”。
如上所述,generics类型的类型擦除不允许这样做。 但你可以达到你想要的效果:
public class BaseClass { public BaseClass(int theParam) { // ...whatever... } public BaseClass() { } } public class DerivedType extends BaseClass { }
现在doIt()方法获取引用的类参数:
public boolean doIt (ArrayList target, Class c) { try { D newElem = c.getDeclaredConstructor(int.class).newInstance(5); } catch (Exception e) {} // ...other code does not matter... return true ; }
你应该这样称呼它:
ArrayList testList = new ArrayList (); testList.add(new DerivedType()); testList.add(new DerivedType()); doIt(testList, DerivedType.class);
希望有所帮助:)
请注意,有人可能真的想要hacky并摆脱class参数并尝试这样做:
public static boolean doIt (ArrayList target) { try { D newElem1 = ((Class ) ((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 ”exception,因为你提到(我没有因为一个被忽视的捕获声明而看到它。 简而言之,Java运行时系统不存储参数化类型(支持向后兼容性;因此将来可能会更改)。
所以,看起来如果不“触动”某些课程就不可能。
但是,除了上述方法之外,我还可以考虑另外两件事 。 首先,如果BaseClass和DerivedType’D’类都实现clone()方法,您可以从数组中获取对象的克隆,然后使用它:
D o = target.get(0); D oNew = (D)((BaseClass)o).clone(); target.add(oNew);
多态性将照顾其余的:)
第二个不是真正的“解决方案”,但如果你想要的只是按类型参数化的对象数组的新实例,则可以使用它。 类型擦除仅适用于参数化类型,但基本数组不会发生(数组在JVM中实现)。 因此,如果我们可以自由更改方法的签名并使用数组就可以了,那么以下方法就可以了:
public 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 l = new ArrayList (Arrays.asList(target)); l.add(newD); } catch (Exception e) { e.printStackTrace(); } return true; }
注意:如果我们不能引入新参数,超级类型令牌将无法解决此问题。 如果我错了,请纠正我。
请考虑编译器在编译时擦除generics信息并将其替换为object。 内部generics只是从java.lang.Object转换为java.lang.Object。
这也是为什么在运行时很难获得通用信息的原因,即使它是可能的。
看到这里: 谷歌 。
在一个说明:如果你需要做这样的事情,通常是糟糕的设计。 我在这种情况下有几次,但我每次都找到了更好的解决方案:)。 所以,只要考虑一下你的代码中是否真的需要这么脏的黑客攻击。
编辑:关于评论部分,需要更详细的解释。
无论如何,应该谨慎使用reflection,因为从软件工程的角度来看,它被认为是糟糕的设计。 为什么? 它可能会引入一些难以发现的错误,因为reflection会改变应用程序的自然流程并使用在开发时并不总是可见的信息。 这突然出现了意想不到的行为。
虽然我没有这方面的正式证据,但我说每次你需要反思时,你的问题还有另一个解决方案(如果生成软件开发是一个选项;))。
所以最后,在99%的情况下,reflection只不过是一个懒惰程序员的肮脏黑客。 这可能与100%的所有程序员都很懒惰这一事实有关,但无论如何。
编辑2:
既然你想要代码:
abstract class Foo { private Class tClass; T field; public void bar(Class clazz) { Type type = getClass().getGenericSuperclass(); if (type instanceof ParameterizedType) { ParameterizedType paramType = (ParameterizedType)type; tClass = (Class ) paramType.getActualTypeArguments()[0]; field = tClass.newInstance(); } } }
(摘自: 这里 )
您无法在此处创建ElemType对象,因为一旦通用代码被实例化,编译器就无法准确知道ElemType将是什么。
为了允许创建ElemType,我将提供某种工厂对象。 您可以使用Javareflection类,但提供自己的工厂基类或接口可能更容易。
想象一下你有这个:
public class DerivedType extends BaseClass { // No more one argument constructor public DerivedType() { super(0); } }
然后是电话
DerivedType d = new DerivedType(5);
无效……