分派调用的过程揭示了面向对象语言的多态性特征,其中重载和重写是其重要组成部分
一个静态分派例子:
public class StaticDispatch {
static abstract class Human
{
}
static class Man extends Human
{
}
static class Woman extends Human
{
}
//重载
public void say(Human guy)
{
System.out.println("human");
}
public void say(Man guy)
{
System.out.println("man");
}
public void say(Woman guy)
{
System.out.println("woman");
}
public static void main(String[] args) {
Human man=new Man();
Human woman=new Woman();
StaticDispatch sr=new StaticDispatch();
sr.say(man);
sr.say(woman);
}
}
输出结果:
为什么实际运行过程中会选择say(Human guy)这个方法 而不是其它方法?
——Human man=new Man();
上述代码中Human称为变量的静态类型(Static Type) 或者叫做外观类型,而后面的Man变量称为变量的实际类型,静态类型和实际类型在程序中可以发生一些变化,
- 静态类型的变化仅仅在使用时发生,变量的静态类型不会被改变,并且最终的静态类型是 编译期可知
- 实际类型变化的结果只有在运行期才能确定,编译器在编译程序并不知道一个待传入对象的实际类型
Human man=new Man();
man=new Woman();//实际类型变化
sr.say((Man) man);//静态类型变化
sr.say((Woman)man);
回到之前的程序
public static void main(String[] args) {
Human man=new Man();
Human woman=new Woman();
StaticDispatch sr=new StaticDispatch();
sr.say(man);
sr.say(woman);
}
Main中 里面2次调用say()方法,在方法接手者已经完全确定是对象sr 的前提下,使用哪个重载版本,完全却决于传入的参数和数据类型。代码中定义了2个静态类型相同(Human) 但实际类型不同的变量,但编译器在重载时是通过参数的静态类型而不是实际类型作为判定依据的 。并且静态类型编译期可知,因此在编译阶段 Javac编译器会根据参数的静态类型选择何种重载版本
所有依赖静态类型来定位方法执行版本的分派动作称为分派,典型的案例就是方法重载。静态分派发生在编译期,因此可以确定静态分派的动作不是由虚拟机来完成的,编译器能够确定出方法的版本,但方法却不一定唯一。