Java反射在使用泛型时获取运行时类型

Java反射在使用泛型时获取运行时类型,java,generics,reflection,Java,Generics,Reflection,我想知道在使用泛型时如何获得程序员编写的运行时类型。例如,如果我有类Main,程序员编写了如下代码 Main<ArrayList<String>> main = new Main<>(); 我只能理解边界类(而不是运行时类)。正如上面的评论所指出的,因为您不能这样做。但在评论中,后续问题是: 我知道编译后泛型会被删除,但我想知道运行时如何抛出ClassCastException?抱歉,如果这是一个愚蠢的问题,但是如果没有任何关于类的信息,它如何知道抛出这个异

我想知道在使用泛型时如何获得程序员编写的运行时类型。例如,如果我有
类Main
,程序员编写了如下代码

Main<ArrayList<String>> main = new Main<>();

我只能理解边界类(而不是运行时类)。

正如上面的评论所指出的,因为您不能这样做。但在评论中,后续问题是:

我知道编译后泛型会被删除,但我想知道运行时如何抛出ClassCastException?抱歉,如果这是一个愚蠢的问题,但是如果没有任何关于类的信息,它如何知道抛出这个异常

答案是,尽管类型参数已从类型中删除,但它仍保留在字节码中

从本质上讲,编译器会转换以下内容:

List<String> list = new ArrayList<>();
list.add("foo");
String value = list.get(0);
这意味着类型
String
不再与字节码中的类型
ArrayList
相关联,但它仍然以类转换指令的形式出现。如果在运行时类型不同,您将得到一个
ClassCastException

这也解释了为什么你可以这样做:

// The next line should raise a warning about raw types
// if compiled with Java 1.5 or newer
List rawList = new ArrayList();

// Since this is a raw List, it can hold any object.
// Let's stick a number in there.
rawList.add(new Integer(42));

// This is an unchecked conversion. Not always wrong, but always risky.
List<String> stringList = rawList;

// You'd think this would be an error. But it isn't!
Object value = stringList.get(0);
//下一行应该会发出关于原始类型的警告
//如果使用Java 1.5或更新版本编译
List rawList=new ArrayList();
//因为这是一个原始列表,所以它可以容纳任何对象。
//让我们在里面放一个数字。
添加(新整数(42));
//这是未经检查的转换。不总是错的,但总是有风险的。
List stringList=rawList;
//你会认为这是个错误。但事实并非如此!
对象值=stringList.get(0);
事实上,如果您尝试它,您会发现您可以安全地将
42
值作为
对象
拉回来,而不会出现任何错误。这是因为编译器没有在这里插入对
字符串的转换,它只是插入对
对象的转换(因为左侧的类型是just
Object
),而从
整数
对象
的转换应该会成功

无论如何,这只是一种冗长的解释方式,说明类型擦除不会擦除对给定类型的所有引用,只擦除类型参数本身

事实上,正如这里提到的一个现已删除的答案,您可以利用这个“残留”类型信息,通过一个名为的技术,您可以使用
ParameterizedType
上的
getActualTypeArguments()
方法访问它

这个小工具的工作方式是创建一个参数化类型的空子类,例如
newtypetoken(){}
。因为这里的匿名类是一个具体类型的子类(这里没有类型参数
T
,它被一个实类型所取代,
String
)类型上的方法必须能够返回实类型(在本例中为
String
)。使用反射可以发现该类型:在本例中,
getActualTypeParameters()[0]
将返回
String.class


Gafter的Gadget可以扩展到任意复杂的参数化类型,并且实际上经常被处理反射和泛型的框架使用。例如,GoogleGuice依赖项注入框架有一个名为的类型正好满足这个目的。

你不能。在Java中,特定的泛型类型是。它们是编译器工具,不是运行时。正如Pshemo所提到的,由于类型擦除,JVM不支持这一点。更多关于中的一些解决方法。您是否希望获取任意泛型类型的类型参数?或者对于您可以更改的特定类型?谢谢大家的关注。我知道编译后泛型会被删除,但我想知道运行时如何抛出ClassCastException?抱歉,如果这是一个愚蠢的问题,但是如果没有任何关于类的信息,它如何知道抛出这个异常。提前谢谢。非常好的回答,我从来没有想过
objectvalue=stringList.get(0)之前的技巧。
List list = new ArrayList();
list.add("foo");
String value = (String) list.get(0); // note the cast!
// The next line should raise a warning about raw types
// if compiled with Java 1.5 or newer
List rawList = new ArrayList();

// Since this is a raw List, it can hold any object.
// Let's stick a number in there.
rawList.add(new Integer(42));

// This is an unchecked conversion. Not always wrong, but always risky.
List<String> stringList = rawList;

// You'd think this would be an error. But it isn't!
Object value = stringList.get(0);