概念
在编程语言和类型论中,多态指同一行为的多种表现形态。
向上转型
先梳理一下几个类与其继承关系,定义一个Animal父类,再分别定义两个子类Cat和Bird子类来继承Animal父类。
class Animal {
}
}
class Cat extends Animal {
}
class Bird extends Animal {
}
1. 语法规则
父类 父类对象 = 子类对象;即 父类引用 引用了 子类对象Animal animal = new Bird;
2. 形式
- 直接赋值
Animal animal = new Bird;
- 方法传参
public class Test {
public static void main(String[] args) {
Bird bird = new Bird("圆圆");
feed(bird);
}
public static void feed(Animal animal) {
animal.eat("谷子");
}
}
- 方法返回
public class Test {
public static void main(String[] args) {
Animal animal = findMyAnimal();
}
public static Animal findMyAnimal() {
Bird bird = new Bird("圆圆");
return bird;
}
}
3. 动态绑定/运行时绑定
首先梳理一下父子类的关系:
class Animal { //父类
public void eat() {
System.out.println("Animal");
}
}
class Cat extends Animal { //子类
@Override
public void eat() {
System.out.println("Cat");
}
}
class Bird extends Animal { //子类
public void fly() {
System.out.println("正在飞!");
}
@Override
public void eat() {
System.out.println("Bird");
}
}
- 若子类中重写了父类定义的方法,那么父类的引用将调用子类的同名方法。
子类Cat和Bird中都重写了父类定义的eat()方法,所以父类animal的引用调用的是子类中的eat()方法;Animal animal1= new Cat;
Animal animal2= new Bird;
animal1.eat();
--> Catanimal2.eat();
--> Bird - 编译时显示调用的是父类的方法,但在运行结果中显示调用的是子类的同名方法。
下面我们可以简单的测试一下:
public static void main(String[] args) {
Animal animal = new Cat("花花");
animal.eat();
}
在Terminal命令框中cd进入相应目录下,javac -encoding utf-8 TestDemo.java编译java文件,javap -c TestDemo查看编译器为我们生成的字节码。
从上图的汇编代码中我们可以看到编译时调用的是父类的方法;但运行时的结果是Cat。
- 在构造函数中,也会发生动态绑定。
class B { //step1
public B() { //父类构造函数
func();
}
public void func() {
System.out.println("B.func()");
}
}
class D extends B {
@Override //step2
public void func() { //父类方法的重写
System.out.println("D.func() ");
}
}
public class Test {
public static void main(String[] args) {
D d = new D();
}
}
//D.func()
我们分析一下上述代码,在主函数中new了一个对象d,那就先需要执行D类的构造函数,(D类的构造函数缺省),而D类作为B类的子类,在构造时应该先帮助父类B类进行构造,于是先执行B类的构造函数,由于父类的构造函数中有被子类重写的普通方法,这时就会发生动态绑定,去执行D类的重写方法。
多态存在的必要条件
- 继承关系
- 方法重写
- 父类引用指向子类对象,即向上转型
分类
重写式多态与重载式多态
优点
- 降低了类的使用成本
- 降低了圈复杂度,避免if-else的大量使用
- 可扩展能力强