锁定老帖子 主题:【解惑】JVM如何理解Java泛型类
该帖已经被评为精华帖
|
|
---|---|
作者 | 正文 |
发表时间:2009-12-16
最后修改:2010-07-14
//泛型代码 public class Pair<T>{ private T first=null; private T second=null; public Pair(T fir,T sec){ this.first=fir; this.second=sec; } public T getFirst(){ return this.first; } public T getSecond(){ return this.second; } public void setFirst(T fir){ this.first=fir; } } 上面是一个很典型的泛型(generic)代码。T是类型变量,可以是任何引用类型。
1、Generic class 创建对象
2、JVM如何理解泛型概念
—— 类型擦除
//编译阶段:类型变量的擦除 public class Pair{ private Object first=null; private Object second=null; public Pair(Object fir,Object sec){ this.first=fir; this.second=sec; } public Object getFirst(){ return this.first; } public void setFirst(Object fir){ this.first=fir; } }
3、泛型约束和局限性—— 类型擦除所带来的麻烦 (1) 继承泛型类型的多态麻烦。(—— 子类没有覆盖住父类的方法 )
看看下面这个类SonPair class SonPair extends Pair<String>{ public void setFirst(String fir){....} } 很明显,程序员的本意是想在SonPair类中覆盖父类Pair<String>的setFirst(T fir)这个方法。但事实上,SonPair中的setFirst(String fir)方法根本没有覆盖住Pair<String>中的这个方法。
这对于多态来说确实是个不小的麻烦,我们看看编译器是如何解决这个问题的。 编译器
会自动在
SonPair中生成一个桥方法(bridge method
)
:
问题还没有完,多态中的方法覆盖是可以了,但是桥方法却带来了一个疑问: 现在,假设 我们还想在 SonPair 中覆盖getFirst()方法呢? class SonPair extends Pair<String>{ public String getFirst(){....} } 由于需要桥方法来覆盖父类中的getFirst,编译器会自动在SonPair中生成一个 public Object getFirst()桥方法。
①String getFirst() // 自己定义的方法 ②Object getFirst() // 编译器生成的桥方法
事实上有一个知识点可能大家都不知道:
(2) 泛型类型中的方法冲突
还是来看一段代码: //在上面代码中加入equals方法 public class Pair<T>{ public boolean equals(T value){ return (first.equals(value)); } } 这样看似乎没有问题的代码连编译器都通过不了: 【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。 编译器说你的方法与Object中的方法冲突了。这是为什么?
开始我也不太明白这个问题,觉得好像编译器帮助我们使得equals(T)这样的方法覆盖上了Object中的equals(Object)。经过大家的讨论,我觉得应该这么解释这个问题?
首先、我们都知道子类方法要覆盖,必须与父类方法具有相同的方法签名(方法名+参数列表)。而且必须保证子类的访问权限>=父类的访问权限。这是大家都知道的事实。 然后、在上面的代码中,当编译器看到Pair<T>中的equals(T)方法时,第一反应当然是equals(T)没有覆盖住父类Object中的equals(Object)了。 接着、编译器将泛型代码中的T用Object替代(擦除)。突然发现擦除以后equals(T)变成了equals(Object),糟糕了,这个方法与Object类中的equals一样了。基于开始确定没有覆盖这样一个想法,编译器彻底的疯了(精神分裂)。然后得出两个结论:①坚持原来的思想:没有覆盖。但现在一样造成了方法冲突了。 ②写这程序的程序员疯了(哈哈)。
再说了,拿Pair<T>对象和T对象比较equals,就像牛头对比马嘴,哈哈,逻辑上也不通呀。
(3) 没有泛型数组一说
Pair<String>[] stringPairs=new Pair<String>[10]; Pair<Integer>[] intPairs=new Pair<Integer>[10]; 这种写法编译器会指定一个Cannot create a generic array of Pair<String>的错误
我们说过泛型擦除之后,Pair<String>[]会变成Pair[],进而又可以转换为Object[]; 假设泛型数组存在,那么 Object[0]=stringPairs[0]; Ok Object[1]=intPairs[0]; Ok 这就麻烦了,理论上将Object[]可以存储所有Pair对象,但这些Pair对象是泛型对象,他们的类型变量都不一样,那么调用每一个Object[]数组元素的对象方法可能都会得到不同的记过,也许是个字符串,也许是整形,这对于JVM可是无法预料的。
记住: 数组必须牢记它的元素类型,也就是所有的元素对象都必须一个样,泛型类型恰恰做不到这一点。即使Pair<String>,Pair<Integer>... 都是Pair类型的,但他们还是不一样。
声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2009-12-16
学习了 谢谢
|
|
返回顶楼 | |
发表时间:2009-12-16
很透彻啊,对泛型的理解又深了一层,
请问楼主,这些知识是平时积累的吗? 还是有什么地方对这些有比较深刻的讲解的? |
|
返回顶楼 | |
发表时间:2009-12-16
不错 平时也没注意过 学习了
|
|
返回顶楼 | |
发表时间:2009-12-16
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? 不要光看提示信息,就想当然。 |
|
返回顶楼 | |
发表时间:2009-12-17
lishuaibt 写道 不错 平时也没注意过 学习了
我也是啊 对泛型有了新的体会,不过有些概念还需要加深! |
|
返回顶楼 | |
发表时间:2009-12-17
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()不也是一样的吗,为什么没有说冲突呢? 还希望牛人指点。谢谢大家 |
|
返回顶楼 | |
发表时间:2009-12-17
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? 不要光看提示信息,就想当然。 是我问题没有阐述清楚,我的意思是,这样Pair<T>中的equals方法不是正好override Object中的equals方法吗,为什么会有冲突,然后编译器说没有覆盖。不太明白?? |
|
返回顶楼 | |
发表时间:2009-12-17
eivenchan 写道 很透彻啊,对泛型的理解又深了一层,
请问楼主,这些知识是平时积累的吗? 还是有什么地方对这些有比较深刻的讲解的? 没事喜欢看些Java方面的书,最近在看Core Java和深入JVM,有新体会就拿上来和大家分享。即增加自己学习的兴趣,又能够和大家交流,有的时候还能够满足一下自己虚荣的心态。哈哈.... 钻研精神+分享心理=大师,中国真正的大师太少了。 |
|
返回顶楼 | |
发表时间:2009-12-17
最后修改:2009-12-17
Heart.X.Raid 写道
在new的时候加上具体类型的方式,在JAVA7里有新的改进:
1. Map<String, List<String>> anagrams = new HashMap<String, List<String>>(); becomes: Map<String, List<String>> anagrams = new HashMap<>();
估计做法跟LZ说的相同,不加<>一样会出错。。。
|
|
返回顶楼 | |