java 协变式覆盖与泛型重载

整理两篇文章《Java Generics and Collections》读书笔记三:协变式覆盖与泛型重载  与   关于泛型和重载的小问题

《Java Generics and Collections》读书笔记三:协变式覆盖与泛型重载 :http://blog.csdn.net/jk88811/article/details/1794866

 关于泛型和重载的小问题

http://blog.csdn.net/zzsyzjb/article/details/6024178

1.协变式覆盖

1.协变式覆盖
在Java1.4及以前,子类方法如果要覆盖超类的某个方法,必须具有完全相同的方法签名,包括返回值也必须完全一样。
Java5.0放宽了这一限制,只要子类方法与超类方法具有相同的方法签名,或者子类方法的返回值是超类方法的子类型,就可以覆盖 。这样有什么好处呢?以Object类的clone方法为例:
class  Object {
 ...
 
public  Object clone() { ... }
}
在5.0以前,如果子类需要重载clone方法,必须像下面这样写代码:
class  Point {
 
public   int  x;
 
public   int  y;
 
public  Point( int  x,  int  y) {  this .x = x;  this .y = y; }
 
public  Object clone() {  return   new  Point(x,y); }
}
虽然在我们的Point类里,clone方法总是返回一个Point类型的对象,但却必须把返回类型写成Object,在外部使用clone方法时也必须使用恼人的强制类型转换。
在Java5.0以后,我们就可以利用新的覆盖规则,像下面这样编写代码:
class  Point {
 
public   int  x;
 
public   int  y;
 
public  Point( int  x,  int  y) {  this .x = x;  this .y = y; }
 
public  Point clone() {  return   new  Point(x,y); }
}
这样,我们就可以直接使用Point p2 = p1.clone(); 而不用强制类型转换了。

2.泛型重载
       Java的方法重载一般指在同一个类中的两个同名方法,规则很简单:两个方法必须具有不同的方法签名。换句话说,就是这两个方法的参数必须不相同,使得编译器能够区分开这两个重载的方法。由于编译器不能仅仅通过方法的返回值类型来区分重载方法,所以如果两个方法只有返回类型不同,其它完全一样,编译是不能通过的。
在泛型方法的重载时,这个规则稍微有一点变化。先看下面代码:
class  Overloaded {
 
public   static   int  sum(List < Integer >  ints) {
    
int  sum  =   0 ;
    
for  ( int  i : ints) sum  +=  i;
    
return  sum;
 }
 
public   static  String sum(List < String >  strings) {
    StringBuffer sum 
=   new  StringBuffer();
    
for  (String s : strings) sum.append(s);
    
return  sum.toString();
 }
}
上面是两个泛型方法的重载例子,由于Java的泛型采用擦除法实现,List<Integer>和List<String>在运行时是完全一样的,都是List类型。也就是,擦除后的方法签名如下:
int  sum(List)
String sum(List)
Java允许这两个方法进行重载,虽然它们的方法签名相同,只有返回值类型不同。这在两个普通方法的重载中是不允许的。当然了,如果两个泛型方法的参数在擦除后相同,而且返回值类型也完全一样,那编译肯定是不能通过的。
类似地,一个类不能同时继承两个具有相同擦除类型的父类,也不能同时实现两个具有相同擦除的接口。如Class A implements Comparable<Integer>, Comparable<Long>。
总结一下,两个泛型方法在擦除泛型信息后,如果具有相同的参数类型,而返回值不一样,是可以进行重载的。Java有足够的信息来区分这两个重载的方法。

为什么会导致这和情况?

我们说的泛型、重载是java语言级别的,但“擦除”技术是关于实现的,它关系到合法class文件的生成,而合法的class文件才能被jvm接受

jvm本来就支持签名相同,但返回类型不同的方法存在。参考《深入java虚拟机》第二版中文版的6.6节:

    “在java源文件的同一个类里,如果声明了两个相同名字和相同参数类型、但返回值不同的方法,这个程序将无法编译通过。在java程序设计语言中,不能仅仅通过返回值的不同来重载方法。但是这样的两个方法可以和谐地在一个class文件中共存。”

     在java语言角度的添加这种限制也是自然的。比如两个方法:

void test(int i);

int test(int i);

编译器不能确定到底应该调用哪个方法,所以这种情况在java中不允许存在。

但是,对于这两个方法test(ArrayList<String> list)和test(ArrayList<Integer> list),在java语言的级别,这是合法的重载,因为编译器可以通过参数类型信息来确定调用哪个版本再加上返回类型不同,经过编译和类型擦除得到的两个方法是可以在class文件中共存的。这样问题就解决了

比如我们写两个方法,

 public void test(ArrayList<String> list){}
 public void test(ArrayList<Integer> list){}

好,这两个方法是合法的重载吧。从语言角度讲,我们可以说是。但是,经过擦除之后,这两个方法就是完全相同的了,如果编译器支持这种情况,那生成的class文件就是jvm不支持的了。那咋办?编译拒绝通过



评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值