简体   繁体   English

如何在Java中实例化泛型方法参数的实例?

[英]How do I instantiate an instance of a generic method parameter in Java?

Consider the following code: 请考虑以下代码:

// ...
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 ;
}

// ..

How can I create an instance of type ElemType in doIt ? 如何在doIt创建ElemType类型的实例?

The construct shown yields the error indicated. 显示的构造产生指示的误差。

ElemType.newInstance does not exist, which surprises me. ElemType.newInstance不存在,这让我感到惊讶。

I've read practically all FAQs, answers and googleable material, but I cannot find anything helpful. 我几乎阅读了所有常见问题解答,答案和googleable资料,但我找不到任何有用的信息。

EDIT: Yes I know reflection has its downsides, and is not the ultimate solution, for numerous reasons. 编辑:是的我知道反思有它的缺点,并且由于种种原因不是最终的解决方案。 The question is not "should I do it", but "how would I do it". 问题不是“我应该这样做”,而是“我该怎么办”。

Please consider that generic-informations are erased by the compiler at compile-time and are replaced with object. 请考虑编译器在编译时擦除泛型信息并将其替换为object。 Internally generics is just casting from and to java.lang.Object. 内部泛型只是从java.lang.Object转换为java.lang.Object。

This also why it's difficult to obtain generic information at runtime, even though it's possible. 这也是为什么在运行时很难获得通用信息的原因,即使它是可能的。

See here: Google . 看到这里: 谷歌

On a persopnal note: If you need to do anything like this, it is usually bad design. 在一个详细说明:如果你需要做这样的事情,通常是糟糕的设计。 I was at this situation a couple of time, but I found a better solution every time :). 我在这种情况下有几次,但我每次都找到了更好的解决方案:)。 So just consider if you really want such a dirty hack in your code. 所以,只要考虑一下你的代码中是否真的需要这么脏的黑客攻击。

Edit: Regarding to the comment section a more detailed explanation is needed. 编辑:关于评论部分,需要更详细的解释。

Reflection in general should be used with caution anyway, since from a software engineering point-of-view it is considered bad design. 无论如何,应该谨慎使用反射,因为从软件工程的角度来看,它被认为是糟糕的设计。 Why? 为什么? It might introduce some hard to find bugs, since reflection alters the natural flow of your application and uses informations which are not always visible to you at development-time. 它可能会引入一些难以发现的错误,因为反射会改变应用程序的自然流程并使用在开发时并不总是可见的信息。 This screams out for unexpected behavior. 这突然出现了意想不到的行为。

And even though I have no formal proof for this, but I state that each time you need reflection, there is another solution for your problem (if generative software-development is an option ;)). 即使我没有这方面的正式证据,但我说每次你需要反思时,你的问题还有另一个解决方案(如果生成软件开发是一个选项;))。

So in the end, in 99% of all cases reflection is nothing more then a dirty hack of a lazy programmar. 所以最后,在99%的情况下,反射只不过是一个懒惰程序员的肮脏黑客。 This might be related to the fact that 100% of all programmars are lazy, but anyway. 这可能与100%的所有程序员都很懒惰这一事实有关,但无论如何。

Edit 2: 编辑2:

Since you want the code anyway: 既然你想要代码:

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();
        }
    }
}

(taken from: Here ) (摘自: 这里

As mentioned, type erasure of generic types does not allow that. 如上所述,泛型类型的类型擦除不允许这样做。 But you can achieve what you want like this: 但是你可以达到你想要的效果:

public class BaseClass {

  public BaseClass(int theParam) {
    // ...whatever...
  }
  public BaseClass() {  
  }          
}

public class DerivedType extends BaseClass {
}

And now doIt() method gets the class argument for reference: 现在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 ;
}

And you should call it like this: 你应该这样称呼它:

    ArrayList<DerivedType> testList = new ArrayList<DerivedType>();
    testList.add(new DerivedType());
    testList.add(new DerivedType());
    doIt(testList, DerivedType.class);

Hope that helps :) 希望有帮助:)

Note that, one may really want to be hacky and get rid of the class parameter and try this: 注意,一个人可能真的想要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 ;
    }
}

In fact I thought so before the second edit :) But this gets a " java.lang.ClassCastException: sun.reflect.generics.reflectiveObjects.TypeVariableImpl cannot be cast to java.lang.Class " exception as you mention (I didn't see it because of an overlooked catch statement). 事实上我在第二次编辑之前就这么认为:)但是这会得到一个“ java.lang.ClassCastException:sun.reflect.generics.reflectiveObjects.TypeVariableImpl不能被强制转换为java.lang.Class ”异常,如你所说(我没有看到它是因为一个被忽视的捕获声明)。 In short, Java runtime system does not store the parameterized types (in favor of backwards compatibility; so this may change in the future). 简而言之,Java运行时系统不存储参数化类型(支持向后兼容性;因此将来可能会更改)。

So, it looks like it is not possible without 'touching' some class. 所以,看起来如果不“触动”某些课程就不可能。

However, other than the mentioned methods, I can think of two more things . 但是,除了上述方法之外,我还可以考虑另外两件事 First, if both the BaseClass and the DerivedType 'D' class implement clone() method, you can get a clone of an object from the array and then use it: 首先,如果BaseClass和DerivedType'D'类都实现了clone()方法,您可以从数组中获取对象的克隆,然后使用它:

         D o = target.get(0);

         D oNew = (D)((BaseClass)o).clone();
         target.add(oNew);

Polymorphism will take care of the rest :) 多态性将照顾其余的:)

The second one is not a real 'solution', but can be used if all you want is a new instance for an array of objects parameterized by type. 第二个不是真正的“解决方案”,但如果你想要的只是按类型参数化的对象数组的新实例,则可以使用它。 Type Erasure only happens for parameterized types, but it does not happen for basic arrays (arrays are reified in JVM). 类型擦除仅发生于参数化类型,但它并不适用于基本阵列发生(数组在JVM 物化 )。 So if we have the freedom to change the signature of the method and working with arrays is ok, then the following would work: 因此,如果我们可以自由更改方法的签名并使用数组就可以了,那么以下方法就可以了:

    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;
}

Note: Super type tokens would not work for this problem if we cannot introduce new parameters. 注意:如果我们不能引入新参数,超级类型令牌将无法解决此问题。 Please correct me if I'm wrong. 如果我错了,请纠正我。

You can't create an ElemType object here since the compiler can't know exactly what an ElemType will be once the generic code is instantiated. 您无法在此处创建ElemType对象,因为一旦实例化通用代码,编译器就无法准确知道ElemType将是什么。

To allow creation of an ElemType, I would supply a factory object of some sort. 为了允许创建ElemType,我将提供某种工厂对象。 You could use a Java reflection class, but it's probably easier to supply your own factory base class or interface. 您可以使用Java反射类,但可能更容易提供您自己的工厂基类或接口。

Imagine that you have this: 想象一下你有这个:

public class DerivedType extends BaseClass
{
    // No more one argument constructor
    public DerivedType() {
        super(0);
    }
}

Then the call 然后是电话

DerivedType d = new DerivedType(5);

is not valid... 无效......

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM