Java 泛型 协变式覆盖和泛型重载
Java 泛型 协变式覆盖和泛型重载
@author ixenos
1.协变式覆盖(Override)
在JDK 1.4及以前,子类方法如果要覆盖超类的某个方法,必须具有完全相同的方法签名,包括返回值也必须完全一样。
JDK 5开始,只要子类方法与超类方法具有相同的方法签名,或者子类方法的返回值是超类方法的子类型(增加了对协变返回值的支持),就可以覆盖。这样有什么好处呢?以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.泛型重载(overload)
Java的方法重载一般指在同一个类中的两个同名方法,规则是:两个方法必须具有不同的方法签名。因此形式参数必须不相同,使得编译器能够区分开这两个重载的方法。由于编译器不能仅仅通过方法的返回值类型来区分重载方法,所以如果两个方法只有返回类型不同,其它完全一样,编译是不能通过的。(泛型、重载是java语言级别的,但“擦除”技术是关于实现的,它关系到合法class文件的生成,而合法的class文件才能被jvm接受,jvm本来就支持签名相同,但返回类型不同的方法存在。)
在java语言角度的添加这种限制也是自然的。比如两个方法:
void test(int i);
int test(int i);
编译器不能确定到底应该调用哪个方法,所以这种情况在java中不允许存在。
但是,对于这两个方法test(ArrayList<String> list)和test(ArrayList<Integer> list),在java语言的级别,即编译时,也可以是合法的重载!
因为编译器可以通过参数类型信息来确定调用哪个版本。再加上返回类型不同,经过编译和类型擦除得到的两个方法是可以在class文件中共存的。这样问题就解决了。
在泛型方法的重载时,这个规则变化如下:
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)
JVM允许这两个方法进行重载(overload!),虽然它们的方法签名相同(形参),只有返回值类型不同。这在两个普通方法的重载中是不允许的。
当然了,如果两个泛型方法的参数在擦除后相同,而且返回值类型也完全一样,那编译肯定是不能通过的。
类似地,一个类不能同时实现两个具有相同擦除的接口。如Class A implements Comparable<Integer>, Comparable<Long>。
总结一下,
如果两个泛型方法在擦除泛型信息后,如果只是具有相同的参数类型,而返回值不一样,就可以进行重载;
2016-09-05 17:39:18更新:
此类泛型重载在JDK 1.7及以上编译时已不允许。
JDK7、8是不可以编译的,需要用JDK6才可以(答案中的均使用oracle jdk提供的编译器)。首先,按道理这个本来就应该报错,从Java语言层面来说,方法重载依赖于相同的方法名、不同的参数个数、类型、顺序,而List<Integer>和List<String>类型擦除后都为List<E>,从而不符合方法重载的要求。
但是,为什么会说这种依赖返回值可以通过甚至正常运行,原因在于,编译后的俩个方法在class中的signature分别为(Ljava/util/List<Ljava/lang/Integer;>;)I
(Ljava/util/List<Ljava/lang/String;>;)Ljava/lang/String;
它们可以合法的共存在一个class文件中。
从jdk7开始呢,编译期做了check,保证了behavior一致,所以报错
作者:葫芦娃
链接:https://www.zhihu.com/question/37802781/answer/75883080
来源:知乎
著作权归作者所有,转载请联系作者获得授权。
Java 泛型 协变式覆盖和泛型重载的更多相关文章
- Java基础之十五 泛型
第十五章 泛型 一般的类和方法,只能使用具体的类型:要么是基本类型,要么是自定义类型.如果要编写可以应用于多种类型的代码,这种刻板的限制对代码的束缚就会很大. 在面对对象编程语言中,多态算是一种泛化机 ...
- 【java基础学习】-【泛型】
参考以下几位同学的总结来学习: http://www.cnblogs.com/lwbqqyumidi/p/3837629.html#!comments http://www.weixueyuan.ne ...
- Java语法糖3:泛型
泛型初探 在泛型(Generic type或Generics)出现之前,是这么写代码的: public static void main(String[] args) { List list = ne ...
- Java笔记(五)泛型
泛型 一.基本概念和原理 泛型将接口的概念进一步延申,“泛型”的字面意思是广泛的类型. 类.接口和方法都可以应用于非常广泛的类型,代码与它们能够操作 的数据类型不再绑定到一起,同一套代码可以应用到多种 ...
- Java编程的逻辑 (37) - 泛型 (下) - 细节和局限性
本系列文章经补充和完善,已修订整理成书<Java编程的逻辑>,由机械工业出版社华章分社出版,于2018年1月上市热销,读者好评如潮!各大网店和书店有售,欢迎购买,京东自营链接:http:/ ...
- Java高质量代码之 — 泛型与反射
在Java5后推出了泛型,使我们在编译期间操作集合或类时更加的安全,更方便代码的阅读,而让身为编译性语言的Java提供动态性的反射技术,更是在框架开发中大行其道,从而让Java活起来,下面看一下在使用 ...
- 【进阶之路】Java的类型擦除式泛型
Java选择的泛型类型叫做类型擦除式泛型.什么是类型擦除式泛型呢?就是Java语言中的泛型只存在于程序源码之中,在编译后的字节码文件里,则全部泛型都会被替换为原来的原始类型(Raw Type),并且会 ...
- Java核心知识1:泛型机制详解
1 理解泛型的本质 JDK 1.5开始引入Java泛型(generics)这个特性,该特性提供了编译时类型安全检测机制,允许程序员在编译时检测到非法的类型. 泛型的本质是参数化类型,即给类型指定一个参 ...
- Java 8 新特性之泛型的类型推导
1. 泛型究竟是什么? 在讨论类型推导(type inference)之前,必须回顾一下什么是泛型(Generic).泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据 ...
随机推荐
- JavaScript中String.prototype.replace() 方法的使用
摘抄于:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/String/replace ...
- java 异常的捕获及处理
在没有异常处理的程序中如果要回避异常,需要使用大量的判断语句,配合所想到的错误状况来捕捉程序中可能发生的错误.但是这样势必会导致程序运行效率降低.java异常处理机制具有易于使用,可自定义异常类,处理 ...
- Front-End(一)
前端初识 现在网站开发的市场越来越大,个人和企业都有了主页.网络办公的需求,并且随着网站开发前端和后台的工作细分,前端开发的需求也越来越大. 前端的任务是将美工的网页设计使用前端技术尽可能无差别地实现 ...
- ajax客户端请求与服务端响应浅谈
AJAX,即Asynchronous Javascript And XML,AJAX本质是在HTTP协议的基础上以异步的方式与服务器进行通信. 所谓的异步,是指某段程序执行不会阻塞其他程序执行,其表现 ...
- linux命令之ifconfig详细解释
依赖于ifconfig命令中使用一些选项属性,ifconfig工具不仅可以被用来简单地获取网络接口配置信息,还可以修改这些配置. 1.命令格式: ifconfig [网络设备] [参数] 2.命令功能 ...
- form -转载于blfshiye
Form API 表单API 文件夹 1.概述 2.亮点 3.使用方法 4.表单元素 4.1 基本表单元素 4.2 定制表单元素 5.经常使用函数 5.1 add_action_buttons($c ...
- Python基础知识学习_Day1
1,python介绍 诞生于1989年圣诞节,目前越来越受到业界认可.应用领域十分广泛 云计算: 云计算最火的语言, 典型应用OpenStack WEB开发: 众多优秀的WEB框架,众多大型网站均为P ...
- WinForm 进程和线程
进程: //进程用到的类Process,需要进行解析 using System.Diagnostics Process.Start("calc");//Process是非静态方法, ...
- group by 和count 联合使用问题
工作中要根据用户发布的产品数量来排序做分页,使用group by uid 用count(uid) 来统计的数量和想要的数量不正确. count统计的数量是被group by 分组以后每一组中数据的数量 ...
- 灾情巡视C语言代码
/*"水灾巡视问题"模拟退火算法.这是一个推销员问题,本题有53个点,所有可能性大约为exp(53),目前没有好方法求出精确解,既然求不出精确解,我们使用模拟退火法求出一个较优解, ...