返回信息流class Test2<T> {
T id;
public void set(T a){ id = a;}
public T get(){return id;}
}
class Test3 extends Test2<Integer>{
public void set(Integer a){id = a+1;}
}
public class Test{
public static void main(String[] args){
Test3 a = new Test3();
Test2 b =a;
b.set(2);
System.out.print(b.get());
}
}
请问一下各位大神,按照我的理解,
在主函数中,b是Test2原始类型的变量,Test2<T>是个泛型类,如果没有给出具体类型的话,那么,T=Object。
所以b中拥有这样一个方法public void set(Object a);
而a中拥有public void set(Integer a);这个方法
那么请问,b.set(2);这句话中,调用的为什么是a中的set方法?
在我看来,b的set方法和a的set方法因为参数类型不同,不构成重写,所以此处应该没有多态特性,应该就是调用b中set方法。不知道是为什么???
希望大家多多指点,谢谢!
这是一条镜像帖。来源:北邮人论坛 / java / #52538同步于 2016/8/21
该镜像源已超过 30 天没有更新,可能在源站已被删除。
Java机器人发帖
【求助】关于泛型擦除后的多态性问题,求教一例,谢谢
johnson123
2016/8/21镜像同步6 回复
订阅后,新回复会通过你的通知中心匿名送达。
6 条回复
这个问题,后来在网上搜索到一篇文章,看后感觉自己明白了,尝试自己解答一下:
关键词:类型擦除、桥方法。
一点介绍:虚拟机中并没有泛型类型对象,所有的对象都是一样的,都属于普通的类。由于JVM根本不支持泛型类型,是编译器“耍了个花招”,使得似乎存在对泛型类型的支持―它们用泛型类型信息检查所有的代码,但随即“擦除”所有的泛型类型并生成只包含普通类型的类文件。泛型类在Java源码上看起来与一般的类不同,在执行时被虚拟机翻译成对应的“原始类型”。泛型类的类型参数列表被去掉,虚拟机用类型参数的限定类型对使用类型参数的地方进行了替换,如果没有限定类型则使用Object类型进行替换。这个过程就是所谓的“类型擦除”。类型参数如果有多个限定,则使用第一个限定类型做替换。泛型方法也会做相同的替换。
所以,在JVM眼中,Test2长这样:
class Test2 {
Object id;
public void set(Object a){ id = a;} //方法1a
public Object get(){return id;}
}
Test3类继承了Test2类,在JVM层面,进行了类型擦除后,他似乎应该长这样:
class Test3 extends Test2{
public void set(Integer a){id = a+1;} //方法1b
}
之前我的疑问就是,方法1a和1b明明方法签名不同,为什么还出现了神之重写。
关键在于其实在这里编译器做了一些微小的工作,他添加了一个桥方法,使其多了一个方法:
public void set(Object a){set((Integer)a);}//方法1c
正是这个方法1c实现了对Test2类中set方法的重写。也保证了多态的特性。
关于什么是桥方法和更多解释,可以看:http://blog.csdn.net/nothing0318/article/details/7608856
【 在 johnson123 的大作中提到: 】
: [code=java]
: class Test2<T> {
: T id;
: ...................
不过我还是有一个疑问,如果在主函数第三行
b.set(2);
改为
b.set("2");
为什么不会报类型转换异常?毕竟就算是有桥方法转换,内部也还是涉及到一个强制类型转换的问题啊?
因为Test3 是Test2<Integer>子类,所以a对象的T已经是Integer了。这时又用Test2 b =a,父类引用指向子类对象,b指向的实际对象还是a,那么a的T已经是Integer了,所以b的set函数就不是T了,是a的T为Integer的函数
谁说不会报异常,我的就报类型转换异常了啊
【 在 johnson123 的大作中提到: 】
: 不过我还是有一个疑问,如果在主函数第三行
: b.set(2);
: 改为
: ...................