当参数化类型通过层次结构时,在Java中创建泛型类型的实例?

时间:2013-09-01 04:06:57

标签: java generics inheritance reflection

我一直在阅读问题的答案:

Create instance of generic type in Java?

我已经实施了Lars Bohl建议的方法。我修改了他的代码如下:

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

public class ParameterizedTypeEg<E> {
    public Class<E> getTypeParameterClass() {
        Type type = getClass().getGenericSuperclass();
        ParameterizedType paramType = (ParameterizedType) type;
        return (Class<E>) paramType.getActualTypeArguments()[0];
    }
    private static class StringHome extends ParameterizedTypeEg<String> {
        private String _string;
        StringHome (String string) {
            _string = string;
        }
    }
    public static void main(String[] args)
        throws InstantiationException, IllegalAccessException {
        String str = new StringHome("my string").getTypeParameterClass().newInstance();
        String str2 = new ParameterizedTypeEg<String>().getTypeParameterClass().newInstance();
    }
}

这种方法适用于str变量。然后创建str2,使我看起来是相同的类型(ParameterizedTypeEg&lt; String&gt;,这与StringHome基本相同)。但是,该方法对str2不起作用,当我尝试转换(ParameterizedType)类型时抛出ClassCastException。

即使对于str2,我使用String参数化了ParameterizedTypeEg,getGenericSuperclass()返回的内容与str非常不同。此外,在方法中,str2将'this'显示为ParameterizedTypeEg,而对于str,'this'是ParameterizedTypeEg $ StringHome。我想这是问题的根源。为什么Java没有看到str2的泛型类型也已确定?

当参数化类型通过多层次的层次结构传递时,我遇到了同样的问题?即,B级&lt; T>包含A&lt; T>我实例化一个B.在A中,我不能通过使用上述方法确定A的参数化类型来创建一个String对象。该方法在包含层次结构的情况下也会产生异常。这导致了一个问题,因为我希望能够通过多级包含和/或继承传递参数化类型,并且在所有情况下都使用相同的方法生成泛型类型的实例。

谢谢, 约翰

4 个答案:

答案 0 :(得分:1)

通过以下更改,您的代码将起作用:

来自

  String str2 = new ParameterizedTypeEg<String>().getTypeParameterClass().newInstance();

   String str2 = new ParameterizedTypeEg<String>(){}.getTypeParameterClass().newInstance();

这将创建ParameterizedTypeEg的匿名子类。当你打电话

getClass().getGenericSuperclass();
在StringHome上,你得到一个ParameterizedTypeEg&lt; java.lang.String&gt;,这就是你想要的。如果像你一样创建str2,那么该调用只返回Object,因此将其转换为paremeterized类型的尝试失败:

  

线程中的异常&#34; main&#34; java.lang.ClassCastException:   java.lang.Class无法强制转换为java.lang.reflect.ParameterizedType

创建一个匿名子类使得返回ParameterizedTypeEg&lt; java.lang.String中&GT;

这与Google Guice Guave图书馆Type Token 课程中使用的技巧相同,顺便说一下。你写,例如

new TypeToken<List<String>>() {}

而不是

 new TypeToken<List<String>>()  

答案 1 :(得分:0)

getClass().getGenericSuperclass();为您提供超类的详细信息。因此,只有在对参数化超类进行子类化时才会起作用。如果您实例化给定类型参数的参数化超类,它将无法工作。

答案 2 :(得分:0)

  

但是,该方法不适用于str2和ClassCastException   当我尝试施放(ParameterizedType)类型
...........为什么时抛出   Java没有看到为str2确定了泛型类型   也?

正如ParameterizedType的oracle doc中所指定的那样:

  

ParameterizedType表示参数化类型,例如   Collection<String>

但是,ParameterizedTypeEg的超类是Object,它不是Generic Type。因此,Type返回的getClass().getGenericSuperclass();是对象本身的Class。向ParameterizedType类型转换非参数化类型会为ClassCastException提供str

答案 3 :(得分:0)

  

为什么Java没有看到通用类型已经确定   str2呢?

运行时的对象没有任何类型参数信息。基本上,为了让你.newInstance()创建一个对象,你需要在运行时拥有类对象,这意味着你必须以某种方式存储它。

它适用于str的原因正是因为类型String存储在某个地方 - 在类元数据中。类具有包含超类和超接口的元数据,如果它是内部类则包含类,以及字段和方法的类型(返回类型和方法的参数类型)。所有这些信息都通过泛型存储在运行时的字节码中(原因是Java中的文件可以单独编译,因此在编译一个使用已编译文件中的类的文件时,编译器必须能够查看类文件并查看泛型信息),并在运行时通过对类对象的反射提供。

基本上,StringHome类充当String类对象的“存储”,因为String在编译时被硬编码为其超类的类型参数。因此,在运行时,您可以从“存储”中获取它。

  

即,B级&lt; T>包含A&lt; T>我在A中实例化一个B.   我无法通过确定参数化类型来创建String对象   A使用上述方法。

从上面的讨论中,你可能认为关键是在运行时以某种方式“获取”T类的类对象。创建一个子类A<T>的类将无济于事,因为关键是实际的类在编译时是硬编码的,这需要在编译时知道类。我们不知道编译时什么类T,所以我们不能把它放到元数据中;我们只能输“T”,即一个类型变量。

因此,问题又回到需要找到一些方法来传递或传递类型参数所代表的类的类对象。您不能再依赖任何基于编译时的机制了。所以你必须在一个额外的参数或其他东西中传递它。