Java:有界类型的getClass()
当我在使用仿制药时,我发现了一些东西。 在下面的示例中, doStuff1
编译但doStuff2
不编译:
public void doStuff1(T value) { Class theClass = value.getClass(); } public void doStuff2(T value) { Class theClass = value.getClass(); }
所以,我查找了Object.getClass()
的文档,发现了这个:
实际的结果类型是Class <? extends | X |> where | X | 是擦除调用getClass的表达式的静态类型。
这让我有点好奇。 为什么getClass()
这种方式设计? 如果适用的话,我可以理解将类型转换为它们的原始类,但是我没有明显的理由说明为什么它们必须使它同时杀掉T
有没有一个特定的原因,它也可以摆脱它,或者它只是一个普遍的“让我们摆脱一切因为它更容易;谁会永远需要它”的方法?
如果getClass()
返回Class extends X>
Class extends X>
,没有什么不好的事情可以发生; 实际上它会帮助很多用例。
唯一的问题是,它在理论上是不正确的。 如果一个对象是一个ArrayList
,它的class
不能是Class
– 没有这样的类,只有一个Class
。
这实际上与擦除无关。 如果有一天Java获得完整的reified类型, getClass()
应该仍然返回Class extends |X|>
Class extends |X|>
; 但是应该有一个新方法,比如getType()
可以返回更详细的Type extends X>
Type extends X>
。 (但是, getType
可能与许多现有类与自己的getType
方法冲突)
对于时间,因为Class extends X>
Class extends X>
在很多情况下可能很有用,我们可以设计自己的方法来做到这一点
static Class extends X> myGetClass(X x){ ... }
但是可以理解他们不会把这种黑客放在标准的lib中。
你不能确定这个value
实际上是一个T,它也可能是T的子类getClass()
确实返回了“真正的”类value
,但你不能确定它是T,因此它必须读取Class extends T>
Class extends T>
! 这与T是类型参数的事实无关。
[编辑]好吧不太好,我没有意识到method2没有编译。 我认为这是编译器问题。 我明白了
Type mismatch: cannot convert from Class to Class extends T>
我认为编译器只会意识到T对象是Foo。 编译器应该知道getClass()
返回一些Class extends T>
Class extends T>
但似乎只知道它返回一些Class extends Foo>
Class extends Foo>
。 这是否意味着Foo是erasure of the static type
T的erasure of the static type
?
非常好的问题。
javagenerics的整个问题是它们在旧虚拟机中不存在。 Sun决定添加通用支持,而现有虚拟机不支持它们。
这可能是一个大问题,因为当它们发布新的Java时,旧的虚拟机将无法运行为最新版本的Java编写的任何代码,并且它们将无法支持旧版本的旧虚拟机。
因此,他们决定以可以在旧虚拟机中运行的方式实现generics。 他们决定从BYTECODE中删除他们。
所以整个故事都是关于jvms的支持。
编辑:但这可能与这个问题无关。 看kutschkem的回答!
另外,我的猜测是,在第二种情况下,演员是Class extends List>
Class extends List>
为Class extends T>
Class extends T>
。 只有当List直接扩展T(因为它是隐式转换)时才能进行此转换。 但另一方面,T扩展了List。
我认为它与说法相同:
String s = "s"; Object o = s;
在上面的例子中,强制转换是可能的,因为String扩展了Object。 对于反向,您需要一个显式的强制转换。
如果您向程序添加显式强制转换,它将编译:
public void doStuff2(T value) { Class extends T> theClass = (Class extends T>) value.getClass(); }
番石榴reflection工具提供了一些工具来解决这个限制,并帮助您获得有界类型的类型(所以类)。
正如本说明页面第一行所述:
由于类型擦除,您无法在运行时传递generics类对象 – 您可能能够转换它们并假装它们是通用的,但它们实际上并非如此。
Guava提供了TypeToken,它使用基于reflection的技巧来允许您操作和查询generics类型,即使在运行时也是如此。
想法是获取(或创建)有界类型的TypeToken ,然后您可以询问其类型。
我认为这个答案有点偏离主题,因为它没有直接回答问题(’为什么’部分),但它可以修复不可编译的方法,它可以导致代码能够解决问题并给出你找到原因的答案部分,肯定会帮助其他人寻找’Java: – 如何-getClass()的有界类型“