“多态”这个词来源于希腊语,意思是“多种形式”,最早应用于生物学,指同在一个生物群体,各个体之间存在的生理学、形态学和生化学的差异。

在 Java 中,多态是指允许同一程序指令在不同的上下文中实现不同的操作。具体来讲,当一个方法名作为一个指令时,根据执行该方法的对象类型,可能产生不同的动作。

面向对象程序设计中包括两种形式的多态,分别是编译时多态和运行时多态:

编译时多态是通过重载技术实现的,即在一个类中相同的方法名可用来定义不同的方法;

运行时多态是基于继承机制建立的,是在运行时动态产生的多态性。

接下来主要对运行时多态进行讲解,下文提到的多态指的都是运行时多态。

多态是面向对象的重要特性,它可以提高程序的抽象程度和可扩展性,最大程度地降低类和程序模块间的耦合度。

为什么需要多态

下面通过一个现实生活中的例子来认识一下多态,这个例子模拟的是主人喂养宠物,代码如下:

class Pet { // 宠物类

private String name = "无名"; // 昵称

private int health = 100; // 健康值

public void eat() {

}

}

class Dog extends Pet { // 狗类继承自宠物类

public void eat() { // 重写eat()方法

System.out.println("狗狗在吃饭");

}

}

class Cat extends Pet { // 猫类继承自宠物类

public void eat() {

System.out.println("猫咪在吃饭");

}

}

class Master {

private String name;

public void feed(Dog dog) {

dog.eat();

}

public void feed(Cat cat) {

cat.eat();

}

}

在上述代码中,Pet 类是父类,Dog 类和 Cat 类是子类,并且重写了父类的 eat() 方法。Master 类分别为 Dog 类和 Cat 类定义了喂食的方法,这两个方法构成了方法重载,可以实现主人喂养宠物的功能。

但是,假如主人以后要喂养更多的不同种类的宠物时该怎么办呢?比如说,现在主人要喂养鹦鹉,我们除了需要先新增一个 Parrot 类(鹦鹉类)之外,还必须在 Master 类中新增一个给鹦鹉喂食的方法。当需要删除某个宠物类时,则需要进行删除相关宠物类的代码。

那么,有没有更好的解决办法呢?这时候,就可以使用多态进行代码优化。

Java多态的含义

编程是一个将具体世界进行抽象化的过程,多态是抽象化的一种体现,即将一系列具体事物的共同点抽象出来,再通过这个抽象的事物,与不同的具体事物进行对话。对不同类的对象发出相同的消息,将会有不同的行为。

例如,公司规定所有员工在九点钟开始工作,而不需要由专业领导对财务人员说“开始财务工作”、对行政人员说“开始行政工作”…,使用专业术语来定义多态就是:同一个引用类型,使用不同的实例可以执行不同的操作,即父类引用子类对象。

下面通过代码来理解一下多态,参考代码如下:

Pet p = new Dog();

p.eat();

在上述代码中,我们将子类对象赋值给一个父类对象,这就是所谓的父类引用子类对象,或者说一个父类的引用指向了一个子类对象。代码在执行时调用的都是子类中重写过的 eat() 方法,而不是父类中的 eat() 方法,这就是多态。

Java多态的实现

同一个父类派生出的多个子类可被当作同一种类型,这样使用相同的代码就可以处理所有子类的对象。

在文章开头的实例程序中,Pet 类是父类,Dog 类和 Cat 类是子类,针对父类中的 eat() 方法,子类都重写了这个方法。对于代码“p.eat()”,当前赋值的是 Dog 类的对象,当然也可以是 Cat 类或其他 Pet 类的子类对象,即可能会得到多种运行结果,具体的结果取决于程序运行时父类对象 p 所指向对象的类型。

接下来,通过案例来演示多态的具体实现:

class Pet { // 宠物类

private String name = "无名"; // 昵称

private int health = 100; // 健康值

public void eat() {}

}

// Dog类、Cat类重写父类的eat()方法

class Dog extends Pet { // 狗类继承自宠物类

public void eat() { // 重写eat()方法

System.out.println("狗狗在啃骨头");

}

}

class Cat extends Pet { // 猫类继承自宠物类

public void eat() {

System.out.println("猫咪在吃鱼干");

}

}

// 父类引用子类对象

class Master {

private String name; // 主人的姓名

// 通过传递参数实现父类引用子类对象

public void feed(Pet p) {

p.eat(); // 主人喂宠物,具体类型由传入的类型决定

}

}

public class Demo {

public static void main(String[] args) {

Pet d = new Dog();

Pet c = new Cat();

Master m = new Master();

m.feed(d);

m.feed(c);

}

}

程序的运行结果如下:

狗狗在啃骨头

猫咪在吃鱼干

通过程序结果可以发现,使用多态解决了主人喂养宠物的问题。这时不管主人将来喂养多少种宠物,我们只需要新增子类继承 Pet 类并重写 eat() 方法就可以了,而 Master 类中始终只需要一个 feed() 方法。

使用多态可以提高代码的可扩展性和可维护性,使用父类作为方法形参是实现多态的常用方式。这里要注意的是,在进行方法调用时必须调用子类重写过的父类方法,子类中独有的方法是不能通过父类引用调用到的。