请考虑以下代码:
// ...
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 ;
}
// ..
如何在ElemType
中创建doIt
类型的实例?
显示的构造产生了指示的错误。
ElemType.newInstance
不存在,这让我感到惊讶。
我几乎阅读了所有常见问题解答,答案和googleable资料,但我找不到任何有用的信息。
编辑: 是的我知道反思有它的缺点,并且由于种种原因而不是最终的解决方案。问题不是“我应该这样做”,而是“我该怎么做”。
答案 0 :(得分:3)
请考虑编译器在编译时擦除泛型信息并将其替换为object。 内部泛型只是从java.lang.Object转换为。
这也是为什么在运行时很难获得通用信息的原因,即使它是可能的。
见这里:Google。
在一份信息中: 如果你需要做这样的事情,通常是糟糕的设计。 我在这种情况下有几次,但我每次都找到了更好的解决方案:)。 所以,只要考虑一下你的代码中是否真的需要这么糟糕的黑客攻击。
修改强> 关于评论部分,需要更详细的解释。
无论如何,应该谨慎使用反射,因为从软件工程的角度来看,它被认为是糟糕的设计。 为什么? 它可能会引入一些难以发现的错误,因为反射会改变应用程序的自然流程并使用在开发时并不总是可见的信息。 这突然出现了意想不到的行为。
即使我没有这方面的正式证据,但我说每次你需要反思时,你的问题都有另一种解决方案(如果生成软件开发是一种选择;)。
所以最后,在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();
}
}
}
(取自:Here)
答案 1 :(得分:3)
如上所述,泛型类型的类型擦除不允许这样做。但是你可以达到你想要的效果:
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;
}
注意:如果我们无法引入新参数,超级类型令牌将无法解决此问题。如果我错了,请纠正我。
答案 2 :(得分:2)
你不能在这里创建一个ElemType对象,因为一旦通用代码被实例化,编译器就无法准确知道ElemType是什么。
为了允许创建ElemType,我会提供某种工厂对象。您可以使用Java反射类,但可能更容易提供您自己的工厂基类或接口。
答案 3 :(得分:0)
想象一下,你有这个:
public class DerivedType extends BaseClass
{
// No more one argument constructor
public DerivedType() {
super(0);
}
}
然后是电话
DerivedType d = new DerivedType(5);
无效......