2013-09-01 45 views
1

我一直在读的问题的答案:当参数化类型通过层次结构时,在Java中创建泛型类型的实例?

Create instance of generic type in Java?

我已经实现了由拉尔斯·博尔建议的方法。我改编他的代码如下:

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 < String>,它基本上是一个StringHome相同的东西)。但是,该方法对于str2不起作用,并且在尝试投射(ParameterizedType)类型时抛出ClassCastException。

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

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

感谢, 约翰

回答

1

用下面的改变,你的代码将工作:

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

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

这将创建ParameterizedTypeEg的匿名子类。当您在StringHome调用

getClass().getGenericSuperclass(); 

,你会得到一个ParameterizedTypeEg < java.lang.String中>,这是你想要的。如果创建STR2像你一样,该呼叫只是返回的对象,所以它转换为一个paremeterized类型的尝试失败:

异常在线程“主要” java.lang.ClassCastException: 的java.lang.Class不能转换为java.lang.reflect.ParameterizedType

创建一个匿名子类使得返回ParameterizedTypeEg < java.lang。字符串>

这是在Google Guice Guave库中的Type Token类中使用的相同技巧,顺便说一句。你写,例如

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

,而不是

new TypeToken<List<String>>() 
+0

谢谢。这工作。但是在处理遏制问题时我提到了一个类似的问题,并且我发布了一个与此类似的新问题。请参阅http://stackoverflow.com/questions/18559285/create-instance-of-generic-type-in​​-java-when-parameterized-type-is-contained – Jorocco

0

getClass().getGenericSuperclass();给你超类的细节。因此,它只会在您参数化的超类的子类中起作用。如果您在给定类型参数的情况下实例化参数化超类,它将不起作用。

+0

你能否推荐另一种方法来创建泛型类型的实例,该实例可用于实例化提供类型参数的参数化超类? – Jorocco

+0

由于泛型是通过类型擦除实现的,我不认为你可以在运行时决定价值。可能是你可以引入像'ParameterizedTypeEg(Class clazz)'的构造函数并强制将该类作为参数传递 –

0

然而,这种方法不适用于STR2工作,当我尝试投(ParameterizedType)型
一个ClassCastException 被抛出。 ..........为什么 Java没有看到通用类型已经确定了str2 也?

在Oracle的文档中指定为ParameterizedType

ParameterizedType表示作为 Collection<String>

一个参数化的类型,例如但,超类的ParameterizedTypeEg是对象,这是不通用类型。所以,由getClass().getGenericSuperclass();返回的Type是Object本身的Class。并且将非参数化类型铸造到ParameterizedType会给 str

0

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

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

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

因此,基本上,在StringHome类充当一个“存储”为String类对象,因为String被硬编码为在编译时其超类的类型参数。所以在运行时你可以把它从这个“存储”中解放出来。

即,B类< T>包含< T>和我实例化一个B.在A, 我不能通过确定参数化的类型A,使用上述方法的 创建一个字符串对象。

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

所以,问题又一次需要找到某种方式来传递或传输类型参数表示的类的类对象。你不能再依赖任何基于编译时间的机制。所以你必须通过一个额外的参数或其他东西来传递它。