重载
在任何程序设计语言中,一项重要的特性就是名字的运用,通过名字描述自己的系统,可使自己的程序更易人们理解和修改。
对于对象,我们创建一个对象时,会分配一个保存区域的名字。
对于方法,我们新建一个方法时,通过名字代表是哪一种具体的行动。
但是,会出现一种情况,就是相同的名字却表达出不同的含义,换一种话说就是同一个方法名,却执行不同的行动。
对此,大多数程序设计语言(特别是C)要求我们为每个函数都设定一个独一无二的标识符(名字)。也就是说,绝对不能用一个名为print()的函数来显示整数,再用另一个print()显示浮点数——每个函数都要求具备唯一的名字。
在java中,出现上述这种问题最多的是在构造器中,因为构造器的名字必须和类名相同,所以导致只能有一个构造器不同,所以,为了应对多种方式创建一个对象的需求,这里必须要采取重载这个特性。
所以说,重载的作用相当一部分作用是为了让程序中相同的方法名字能执行不同的行为。
重载的概念
Java允许在一个类中,多个方法拥有相同的名字,但在名字相同的时候,通过传入的参数列表不同,编译器会根据实际情况挑选出正确的方法,如果编译器找不到匹配的参数列表的方法,或者可能找出多个,那么就会产生编译错误。—-这个过程就是重载。
重载的作用
为了程序的可读性,方法的重载是在编译时刻就决定调用哪个方法了。重载对于编写结构清晰而简洁的类有很大的作用。
对于程序员来说,重载的作用方便了程序员可以根据不同的参数个数,顺序,类型,自动匹配相同名字的方法。
重载的分类
重载主要分为普通方法的重载和构造器重载。
普通方法的重载(满足以下任意一种 即构成重载 前提是方法名相同):
1、参数个数不同
2、对应位置上的参数类型不同
注意点:不允许参数完全相同而只是返回值不同的情况出现。无法进行编译,编译器出错。
3、可以有不同的访问修饰符
4、可以抛出不同的异常
5、方法能够在一个类中或者在一个子类中被重载
构造器重载:
在构造器中文章有详细介绍。
编译器解析重载的步骤
- 根据调用的方法名,查找是否有定义好的同名方法,如果没有则报错。
- 比较形参和实参的数目是否相等,如果没有则会报错。如果有一个或多个方法符合条件,这些方法进入候选集
- 与候选集中的方法比较参数表,如果对应位置上的每个参数类型完全匹配,或者通过扩展转换相匹配,则该方法成为可行方法,并入可行集。若不存在可行方法,则会报错。
- 在可行集中按照下面的原则选取最佳可行方法,若最佳可行方法为0则会报错,否则最佳可行方法就是最终确定要调用的方法。
选取原则
- 若每一个参数都可以完全匹配,它就是最佳可行方法
- 若某个方法的每一个参数都不比别的方法差,且至少有一个参数比别的方法好,它就是最佳可行方法,这里的差和好是指,完全匹配要比扩展转化好,不过同样是扩展转换,仍然存在好和差的问题,扩展转换有两条路径
byte-short-int-long-float-double
char-int-long-float-double
这两条路径中位于左边的类型都可以转换为右边的类型,不过源类型与目标类型的距离越近,则这种转化就越好。
选取原则例子
show(int a ,int b,int c) //1
show(int a ,int b,double c) //2
show(int a ,double b,double c)//3
show(double a,double b,int c) //4
下面是调用
show(1,2,3);//1,2,3,4都是可行方法所有参数完全匹配1
show(1.0,2.0,3.0);//没有一个可行方法
show(1.0,2,3);//4是最佳可行方法
show(1,2.0,3);//3,4都是可行方法,没有最佳可行方法,报错
区分重载载方法
- 自变量列表(参数列表)个数
- 自变量列表(参数列表)类型
- 自变量列表顺序
基本类型的重载
因为java中基本类型在发生类型转换时,能从一个“较小”的类型自动转换变成一个“较大”的类型。
那么当自动类型转换在涉及到重载的问题时,会造成一些混乱。
如果我们的参数类型“小于”重载方法中使用的参数类型:
那么就会对那种数据类型进行“转型”处理。而此时,char获得的效果稍些不同,这是由于假如没有发现一个准确的char类型匹配,就会转型为int,而这就是最佳选取原则。
如果我们的参数类型“大于”重载方法中使用的参数类型:
那么就必须用括号中的类型名将其转为适当的类型。如果不这样做,编译器会报告错误。因为这是一种“缩小”转换,所以在造型或转型的过程中可能会丢失一些信息。这正是编译器强迫我们明确表达想要转型的原因。
为什么不能用返回值作为重载的条件?
首先来看两个方法:
void f() {}
int f() {}
这两个方法采用了返回值的不同来作为区分条件,这样做,如果编译器可根据上下文明确判断出返回值类型,那么这样做完全没问题。
但我们在很多时候,调用方法是忽略返回值的,比如构造方法中的重载,没有返回值的重载。在这种情况下,我们通常称之为“为它的副作用去调用一个方法”这里有两个点:
它是谁?
在这里,它指的就是调用方法
副作用是什么?
一个方法的执行,如果在返回一个值之外还导致某些外部“状态”发生变化,则称该方法产生了副作用。这里所谓“状态”发生变化,可以是实例域或静态变量被修改、方法的实参被修改(Java 中不会出现这种情况。但是实参为引用时,其指向的对象可能被修改从而产生副作用)、将数据传递给显示器、打印机或存入文件中等等。
没有返回值的方法必然有副作用,除非它的方法体是空的或者方法没有意义。所以,没有返回值的方法、有返回值但有副作用的方法称为过程(procedure)或命令(command)。简言之,有副作用的方法称为过程。
所以,对于没有返回值的方法,我们关心的不是返回值,而是方法调用的过程和其他效果。由于存在这一类问题,所以不能根据返回值类型来区分重载的方法。
题外话:严格定义方法的原因
如此严格地定义出方法的概念,是因为方法使得系统的状态稳定,方法的行为容易预测。更进一步,如果函数也就是方法是纯粹的函数(pure function,纯函数)——它的输出值依靠和仅仅依靠其输入、对于相同的输入总是返回相同的值,(由于纯函数的纯粹和无副作用)对纯函数的调用就能够被一个值取代(或者说,将方法视为一个值),这就是函数式编程语言中著名的引用透明(referentialtransparency)特性。