DPM*_*DPM 3 java generics reflection
我想知道如何获得使用泛型时程序员编写的运行时类型。例如,如果我class Main<T extends List<String>>和程序员写了类似的东西
Main<ArrayList<String>> main = new Main<>();
Run Code Online (Sandbox Code Playgroud)
我如何通过反射来了解使用了哪个类扩展List<String>?
我很好奇我怎么能做到这一点。用
main.getClass().getTypeParameters()[0].getBounds[]
Run Code Online (Sandbox Code Playgroud)
我只能理解边界类(而不是运行时类)。
正如上面的注释所指出的,由于类型擦除,您无法执行此操作。但是在评论中,后续问题是:
我知道编译后会删除泛型,但是我想知道ClassCastException如何在运行时抛出?抱歉,如果这是一个愚蠢的问题,但是如果没有有关类的任何信息,它将如何抛出该异常。
答案是,虽然类型参数从擦除类型,它仍然在字节码。
本质上,编译器将其转换为:
List<String> list = new ArrayList<>();
list.add("foo");
String value = list.get(0);
Run Code Online (Sandbox Code Playgroud)
到这个:
List list = new ArrayList();
list.add("foo");
String value = (String) list.get(0); // note the cast!
Run Code Online (Sandbox Code Playgroud)
这意味着该类型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);
Run Code Online (Sandbox Code Playgroud)
确实,如果您尝试一下,您会发现可以安全地将42值作为撤回,Object并且完全没有任何错误。这样做的原因是,编译器没有将强制类型转换插入到String此处-而是只插入了强制类型转换到Object(因为左侧类型为just Object),并且强制转换Integer为Object成功。
无论如何,这只是一个漫长的解释方式,它解释了类型擦除并不会擦除对给定类型的所有引用,而只是擦除类型参数本身。
实际上,作为此处提到的已删除答案,您可以通过称为Gafter's Gadget的技术来利用这种“调查”类型的信息,您可以使用上的getActualTypeArguments()方法进行访问ParameterizedType。
小工具的工作方式是创建一个参数化类型的空子类,例如new TypeToken<String>() {}。由于此处的匿名类是具体类型的子类(此处没有类型参数T,已由实型替换String),类型上的方法必须能够返回实型(在这种情况下String)。使用反射,您可以发现该类型:在这种情况下,getActualTypeParameters()[0]将返回String.class。
Gafter的Gadget可以扩展为任意复杂的参数化类型,并且实际上经常由在反射和泛型上做大量工作的框架使用。例如,Google Guice依赖项注入框架具有一个名为的类型TypeLiteral,正是该类型用于此目的。
| 归档时间: |
|
| 查看次数: |
1177 次 |
| 最近记录: |