1

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

在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 < String >,它基本上与 StringHome 相同)。但是,该方法不适用于 str2,并且当我尝试强制转换 (ParameterizedType) 类型时会引发 ClassCastException。

尽管对于 str2,我已经使用字符串参数化了 ParameterizedTypeEg,但 getGenericSuperclass() 返回的结果与 str 非常不同。此外,在方法 str2 中,“this”显示为 ParameterizedTypeEg,而对于 str,“this”是 ParameterizedTypeEg$StringHome。我想这就是问题的根源。为什么 Java 看不到 str2 的泛型类型也已确定?

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

谢谢,约翰

4

4 回答 4

1

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

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

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

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

getClass().getGenericSuperclass();

在 StringHome 上,您会得到一个 ParameterizedTypeEg <java.lang.String>,这就是您想要的。如果您像以前一样创建 str2,则该调用仅返回 Object,因此尝试将其强制转换为参数化类型失败:

线程“主”java.lang.ClassCastException 中的异常:java.lang.Class 无法转换为 java.lang.reflect.ParameterizedType

创建一个匿名子类会返回 ParameterizedTypeEg < java.lang.String>

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

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

并不是

 new TypeToken<List<String>>()  
于 2013-09-01T05:12:58.487 回答
0

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

于 2013-09-01T04:54:37.613 回答
0

但是,该方法不适用于str2,并且当我尝试强制转换(ParameterizedType)类型时抛出ClassCastException
............为什么Java没有看到已经为str2确定了泛型类型?

如 oracle 文档中为ParameterizedType指定的:

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

但是,超类ParameterizedTypeEg是 Object ,它不是通用类型。所以,Type 返回的getClass().getGenericSuperclass();ClassObject 本身。并且将非参数化类型的类型转换ParameterizedTypeClassCastException.str

于 2013-09-01T05:14:17.487 回答
0

为什么 Java 看不到 str2 的泛型类型也已确定?

运行时的对象没有任何类型参数信息。基本上,.newInstance()要创建一个对象,您需要在运行时拥有类对象,这意味着您必须以某种方式将其存储在某个地方才能获取它。

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

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

也就是说,类 B<T> 包含 A<T>,我实例化了一个 B。在 A 中,我无法通过使用上述方法确定 A 的参数化类型来创建 String 对象。

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

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

于 2013-09-01T10:06:09.507 回答