如何在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); 

无效……