因为我一直使用Java语言,所以一直以为多态是Java中所特有的概念,但是其实在编程语言和类型论中都有多态的定义,所以之前对多态的理解感觉有些不到位,便做个总结来详细说明一下。
以下是维基百科的定义:多态(Polymorphism),指为不同数据类型的实体提供统一的接口,或使用一个单一的符号来表示多个不同的类型。一般情况下,可以把多态分成以下几类:
-
特设多态:为个体的特定类型的任意集合定义一个共同接口。
-
参数多态:指定一个或多个类型不靠名字而是靠可以标识任何类型的抽象符号。
-
子类型:一个名字指称很多不同的类的实例,这些类有某个共同的超类。
多态在编程语言中都有实现,我们可以使用Java来举例:
1.特设多态
概念:多态函数有多个不同的实现,依赖于实参而调用相应版本的函数。
在Java 中方法重载
和方法重写
都是特设多态中的实现方式。在C语言中叫做函数,在Java中叫做方法。在C语言中也可以实现函数重载和函数重写。
方法重载
方法重载指的是在同一个类中,多个方法有同样的名称,但是参数列表不相同的情形。
方法重载是一个编译期概念,因为它是在编译阶段根据参数变量的类型和个数来判断最终要调用哪个方法,所以它也被叫做静态多态。
为什么方法重载中返回值类型和访问修饰符类型不能作为是否方法重载的判断标准呢?
因为Java识别一个方法是通过JVM
来实现的,而JVM
是通过方法签名来决定调用哪个方法,方法签名又是由方法名称 + 参数类型 + 参数个数组成的一个唯一值,如果方法的返回类型也作为方法签名的一部分,那么当程序员写了一个代码去调用“重载”的方法时,JVM
就不能分辨要调用哪个方法了,所以方法返回值和方法的访问修饰符都不能作为是否方法重载的判断标准,而且被重载的方法也可以改变返回类型和访问修饰符。
方法重写
方法重写也可以叫做方法覆盖,我个人感觉方法覆盖更好理解他是什么,它指的是返回类型、函数名和参数都一样,只是方法的实现体不一样,而且需要满足以下条件。
-
访问级别的限制性一定不能比被重写方法的强
-
写的方法能够抛出更少或更有限的异常(也就是说,被重写的方法声明了异常,但重写的方法可以什么也不声明)
-
不能重写被标示为final的方法
-
只能出现在子类和父类中
方法重写也就是Java多态的实现方式。
方法重写是一个运行时概念,因为它是在运行时,根据引用变量所指向的实际对象的类型来调用方法。
2.参数多态
概念:声明或者定义函数(复合类型、变量)的时候不指定具体的类型,而把这些类型作为参数使用,让该定义对各种具体类型都适用。
Java中的泛型就是参数多态的一种。
3.子类型
概念:在面向对象程序设计中,计算机程序运行时,相同的消息可能会送给多个不同的类别的对象,而系统可依据对象所属类别,引发对应类别的方法,而有不同的行为。
这种子类型多态其实就是Java中常见的多态。
在Java中多态的定义为同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。
Java中多态其实是一种运行期的状态。为了实现运行期的多态,或者说是动态绑定,需要满足三个条件:
-
有类继承或者接口实现
-
子类要重写父类的方法
-
父类的引用指向子类的对象
public class Parent{
public void call(){
sout("im Parent");
}
}
public class Son extends Parent{// 1.有类继承或者接口实现
public void call(){// 2.子类要重写父类的方法
sout("im Son");
}
}
public class Daughter extends Parent{// 1.有类继承或者接口实现
public void call(){// 2.子类要重写父类的方法
sout("im Daughter");
}
}
public class Test{
public static void main(String[] args){
Parent p = new Son(); //3.父类的引用指向子类的对象
Parent p1 = new Daughter(); //3.父类的引用指向子类的对象
}
}
这样,就实现了多态,同样是Parent类的实例,p.call 调用的是Son类的实现、p1.call调用的是Daughter的实现。这也被叫做动态多态。
有人说,你自己定义的时候不就已经知道p是son,p1是Daughter了么。但是,有些时候你用到的对象并不都是自己声明的。
比如Spring 中的IOC出来的对象,你在使用的时候就不知道他是谁,或者说你可以不用关心他是谁。根据具体情况而定。