锁定老帖子 主题:【解惑】JVM如何理解Java泛型类
该帖已经被评为精华帖
|
|
---|---|
作者 | 正文 |
发表时间:2009-12-17
引用 (1) Java编译器没有泛型对象这样的一个特殊概念。它会将所有泛型类的对象全部理解成为普通类对象(这一点会在下面详细阐述)。
此处应该是笔误 在最后也提到是Java虚拟机中不存在泛型概念 换言之 运行态没有泛型,泛型只存在于编译期 楼主的文章用词还需要斟酌 否则容易误导别人 另外,编译时也不是完全当作原始类型处理,比较一下泛型生成的class和原始类型生成的class大小就知道了. 编译器定义方法签名是 方法名+参数列表 所以我们不能写出只有返回类型不同的方法 但是虚拟机中方法签名是 返回类型+方法名+参数列表,虚拟机知道如何定位方法. |
|
返回顶楼 | |
发表时间:2009-12-17
Heart.X.Raid 写道 haojia0716 写道 编译器就是这样说的
Name clash: The method equals(T) of type TT<T> has the same erasure as equals(Object) of type Object but does not override it 有什么看不懂的 equals(Object)和equals(T)擦除后是一样的 你自己前面也说到了 这里的T对于编译器来说就是Object 我也很纳闷,擦除后是一样的,那么Pair<T>不是正好覆盖了Object中的equals方法吗,为什么会有冲突。 class A{ public void a(){} } class B extends A{ public void a(){} } B中的a()和A中的a()不也是一样的吗,为什么没有说冲突呢? 还希望牛人指点。谢谢大家 //方法1 public void test(List<String> list) { System.out.println("List<String>"); } //方法2 public ArrayList test(List<Object> list) { System.out.println("List<Object>"); return null; } //方法3 public void test(List<Object> list) { System.out.println("List<Object>"); } //方法4 public ArrayList test(List<String> list) { System.out.println("List<Object>"); return null; } 你只要想通这4个方法 自然就明白了 |
|
返回顶楼 | |
发表时间:2009-12-17
hankesi2000 写道
Heart.X.Raid 写道
3、泛型约束和局限性—— 类型擦除所带来的麻烦 (1) 继承泛型类型的多态麻烦。(—— 子类没有覆盖住父类的方法 )
.................. 编译器
会自动在
SonPair中生成一个桥方法(bridge method
)
:
红色部分的方法应该是setFirst吧?
呵呵,总体写的挺好的!
是的,太谢谢你了,写的太不仔细了。谢谢指出。现在已经改过来了 |
|
返回顶楼 | |
发表时间:2009-12-17
miaow 写道 都擦除了,没什么额外处理
应该有额外处理,所以这几天都在看虚拟机。JVM是知道用什么具体类型来处理Object的 |
|
返回顶楼 | |
发表时间:2009-12-17
liu78778 写道 JackAndroid 写道 haojia0716 写道 编译器就是这样说的
Name clash: The method equals(T) of type TT<T> has the same erasure as equals(Object) of type Object but does not override it 有什么看不懂的 equals(Object)和equals(T)擦除后是一样的 你自己前面也说到了 这里的T对于编译器来说就是Object 如果对编译器来说是一样,那么为什么不算是override? 不要光看提示信息,就想当然。 不能说对编译器来是完全一样, 应该说编译后是完全一样, 而编译时, 编译器会验证重载的一些判定 在这里, 编译器的工作大概有几步: 1. 编译器首先根据方法签名判断子类的equals方法是个新的方法(因为你使用的T作为参数), 与父类Object的equals方法并不产生重载关系 2. 编译器进行泛型擦除 3. 编译器发现擦除之后出现了两个完全一致的equals(Object)方法, 而子类的equal方法是擦除产生的和定义时的equals(T)对应, 这时编译器不知道你到底是想重载还是在定义一个新的equals 4. 可以预期的是, 如果编译器放过这段代码, 那么在调用equals方法时, jvm无法知道到底是在调用哪一个equals方法(因为同时存在两个equals方法, 而却不是重载关系) 所以编译器为确保代码安全, 会报错: 名称冲突:类型 Test<T> 的方法 equals(T)与类型 Object 的 equals(Object)具有相同的擦除,但是未覆盖它 我觉得有道理: 在擦除以前编译器认为equals(T)是一个新方法,没有覆盖到Object中的equals(Object)。当编译器擦除完后发现这个equals(T)变成了equals(Object)。估计把编译器搞疯了。 但是不知道有没有这种想法的佐证:比如编译器是先进行类型检查再进行泛型方法的擦除工作。 |
|
返回顶楼 | |
发表时间:2009-12-17
langyu 写道 【Error】 Name clash: The method equals(T) of type Pair<T> has the same erasure as equals(Object) of type Object but does not override it。
不就表示擦除后的T变成object了,和Object类的equals(Object)方法是一样的,没有进行重写呀 还有其它玄机吗? 是这样的。 既然Pair<T>擦除以后equals方法变成了equals(Object),是不是和父类Object中的equals(Object)一样了,那么子类Pair<T>是不是就覆盖了Object的方法,也就是方法的重写。那么应该是override it。为什么编译器会认为没有覆盖那。 我觉的18楼liu78778的解释可以理解,不过不知道有没有能够证实这个想法的。 还是感谢大家的讨论。 |
|
返回顶楼 | |
发表时间:2009-12-17
qianhd 写道 引用 (1) Java编译器没有泛型对象这样的一个特殊概念。它会将所有泛型类的对象全部理解成为普通类对象(这一点会在下面详细阐述)。
此处应该是笔误 在最后也提到是Java虚拟机中不存在泛型概念 换言之 运行态没有泛型,泛型只存在于编译期 楼主的文章用词还需要斟酌 否则容易误导别人 另外,编译时也不是完全当作原始类型处理,比较一下泛型生成的class和原始类型生成的class大小就知道了. 编译器定义方法签名是 方法名+参数列表 所以我们不能写出只有返回类型不同的方法 但是虚拟机中方法签名是 返回类型+方法名+参数列表,虚拟机知道如何定位方法. 不是笔误,是自己考虑不周全。受教了,谢谢你,我已经修改过来了。 |
|
返回顶楼 | |
发表时间:2009-12-17
http://topic.csdn.net/u/20091216/20/e882995f-8932-4a02-af87-e25229f33735.html?45935
另外,我在也CSDN发帖了,其中3楼和6楼的回答也有道理。大家可以去看看。 |
|
返回顶楼 | |
发表时间:2009-12-17
最后修改:2009-12-17
有个疑问,不知道楼主能不能给解答下。(昨天写代码时遇到的)
接口: interface ConvertMachine<T>{ /** * convert the list to byte stream. * @param list contains media and audio object * @param key file name except expansion name * @return */ ByteBuffer toByteStream(List<T> list,String key); /** * be used to test * @param file */ void reverse(File file); } 实现类: 引用 class CM<MediaBean> implements ConvertMachine<MediaBean> {
@Override public ByteBuffer toByteStream(List<MediaBean> list, String key) { ByteBuffer buf = null; RandomAccessFile forh264 = null; RandomAccessFile foraac = null; File h264 = new File(key + ".h264"); File aac = new File(key + ".aac"); try { int offsetsum = 0; for (MediaBean bean : list) { offsetsum = offsetsum + bean.getOffset(); } //后面省略掉 } @Override public void reverse(File file) { //be used to test ,implements lazy } } 实现类代码,无红色部分,如写红色部分,编译器会绿色位置报错(IDEA)。 十分不解! |
|
返回顶楼 | |
发表时间:2009-12-17
prowl 写道 有个疑问,不知道楼主能不能给解答下。(昨天写代码时遇到的)
接口: interface ConvertMachine<T>{ /** * convert the list to byte stream. * @param list contains media and audio object * @param key file name except expansion name * @return */ ByteBuffer toByteStream(List<T> list,String key); /** * be used to test * @param file */ void reverse(File file); } 实现类: 引用 class CM<MediaBean> implements ConvertMachine<MediaBean> {
@Override public ByteBuffer toByteStream(List<MediaBean> list, String key) { ByteBuffer buf = null; RandomAccessFile forh264 = null; RandomAccessFile foraac = null; File h264 = new File(key + ".h264"); File aac = new File(key + ".aac"); try { int offsetsum = 0; for (MediaBean bean : list) { offsetsum = offsetsum + bean.getOffset(); } //后面省略掉 } @Override public void reverse(File file) { //be used to test ,implements lazy } } 实现类代码,无红色部分,如写红色部分,编译器会绿色位置报错(IDEA)。 十分不解! class CM<MediaBean>有问题: 首先你要搞清楚你想要定义的类型是泛型类还是普通类。 如果是普通类,class CM implements ConvertMachine<MediaBean>就可以了。 如果是泛型类,正如你定义的一样CM<MediaBean>,编译器会把MediaBean理解为类型变量,相当于常用的T,S,K,V之类的符号。 既然编译器已经认定你定义的class CM<MediaBean>是一个泛型类,而且MediaBean是泛型变量的话。那么编译器理解MediaBean bean 也就认为相当于T bean。试问MediaBean(也就是T)在编译阶段不能够确定具体类型的情况下,MediaBean.getOffset()方法在哪里(也就相当于T.getOffset()方法)。 因此,编译器报错:The method getOffset() is undefined for the type MediaBean 注意,你定义的时候MediaBean已经是CM泛型类的类型变量了,再也不是你认为的某一个类了。一定要保持清醒。 |
|
返回顶楼 | |