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