目录
1. 多态实现的三个必要条件
1.1 继承
1.2 向上转型
1.2.1 向上转型发生的时机
1.3 重写
2. 理解多态(多态示例)
3. 使用多态的好处
3.1 类调用者对类的使用成本进一步降低
3.2 简化性
3.3 可扩展能力更强
4. 多态的实现方法
5. 向下转型
多态是指同一个行为具有多个不同表现形式或形态的能力。
当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,再去调用子类的同名方法。
向上转型就是子类对象转换为父类的引用,是天然发生的。
代码示例:
/**
* 向上转型
*/
class Animal {
protected String name;
public Animal(String name) {
this.name = name;
}
public void eat(String food) {
System.out.println(this.name + "正在吃" + food);
}
}
class Bird extends Animal {
public Bird(String name) {
super(name);
}
public void fly() {
System.out.println(this.name + "正在飞 ");
}
}
public class Demo2 {
public static void main(String[] args) {
// 创建一个动物类的对象
Animal animal = new Animal("小动物");
// 创建一个鸟类的对象
Bird bird = new Bird("小鸟");
// 向上转型
Animal animal1 = new Bird("小鸟");
System.out.println(animal.name);
animal.eat("食物");
}
}
上面的代码中,我们有这样的代码:
Bird bird = new Bird("小鸟");
这个代码也可以这样写:
Bird bird = new Bird("小鸟"); Animal bird2 = bird;
或者是:
Animal animal1 = new Bird("小鸟");
这里的 animal1 是一个父类的引用,指向一个子类的实例。这种写法被称为向上转型。
语法:父类名称 父类引用 = new 子类();
1. 直接赋值
Animal animal = new Bird();
2. 方法传参
/**
* 向上转型
*/
class Animal {
protected String name;
public Animal(String name) {
this.name = name;
}
public void eat(String food) {
System.out.println(this.name + "正在吃" + food);
}
}
class Bird extends Animal {
public Bird(String name) {
super(name);
}
public void fly() {
System.out.println(this.name + "正在飞 ");
}
}
class Cat extends Animal{
public Cat(String name) {
super(name);
}
}
public class Demo2 {
public static void main(String[] args) {
// 创建一个动物类的对象
Animal animal = new Animal("小动物");
// 创建一个鸟类的对象
Bird bird = new Bird("小鸟");
Cat cat = new Cat("小猫");
fun(animal,"食物");
fun(cat,"猫粮");
fun(bird,"虫子");
}
public static void fun(Animal animal, String food) {
animal.eat(food);
}
}
Animal 有多个子类,fun 就得重载多少次。
3. 方法返回
此时 findAnimal 返回的是一个 Animal 类型的引用,但是实际上对应的是 Bird 实例。也就是把一个子类对象转换为一个 Animal 父类的引用。
子类实现父类的同名方法,并且参数的类型和个数完全相同,这种情况称为覆写/重写/覆盖。
重写的规则:
当子类对象调用重写的方法时,调用的是子类的方法,而不是父类中被重写的方法。要想在子类调用父类中被重写的方法,则必须使用关键字 super。
重写和重载的区别可以看这篇。
使用多态设计程序。
/**
* 多态
*/
class Shape {
public void draw() {
}
}
class Cycle extends Shape {
@Override
public void draw() {
System.out.println("○");
}
}
class Rect extends Shape {
@Override
public void draw() {
System.out.println("□");
}
}
class Flower extends Shape {
@Override
public void draw() {
System.out.println("♣");
}
}
public class Demo1 {
public static void main(String[] args) {
Shape s1 = new Cycle();
Shape s2 = new Rect();
Shape s3 = new Flower();
drawShape(s1);
drawShape(s2);
drawShape(s3);
}
private static void drawShape(Shape shape) {
shape.draw();
}
}
在上面这个例子中,三个对象都调用了 drawShape 方法,方法参数都是 Shape 类型,我们根据 shape 参数执行 draw 方法时发现,当传入不同对象时,draw 方法表现了不同的行为。当一个引用在不同场景下调用相同方法表现出来的不同行为。这就是多态。
多态能让类的调用者连这个类的类型时什么都不必知道,只需要知道这个对象具有某个方法即可。就好像 Animal 中有成百上千个子类,我们不需要这个类具体是啥,只要是它的子类,我们都可以使用 Aniaml 这个父类来操作子类。
能够降低代码的 "圈复杂度", 避免使用大量的 if - else。
比如让我们现在打印多个形状,如果灭有多态那代码就是:
但是如果使用多态,就不用使用多个 if - else 分支语句,使代码更加简单。
如果没有多态,新增一个子类,调用者就得先了解此类,然后在调用出再新增一个分支,很不方便。
但是如果有多态,当有一个新的子类产生时,由于多态性,调用者不需要改变代码,使用父类就可以方便的扩展新的子类。
向上转型是子类对象转成父类对象,向下转型就是父类对象转成子类对象。
代码示例:
/**
* 向下转型
*/
class Animal2 {
protected String name;
public Animal2(String name) {
this.name = name;
}
public void eat(String food) {
System.out.println("我是一只小动物");
System.out.println(this.name + "正在吃" + food);
}
}
class Bird2 extends Animal2 {
public Bird2(String name) {
super(name);
}
public void eat(String food) {
System.out.println("小李是一只小鸟");
System.out.println(this.name + "正在吃" + food);
}
public void fly() {
System.out.println(this.name + "正在飞");
}
}
public class Demo3 {
public static void main(String[] args) {
Animal2 animal2 = new Bird2("小李");
animal2.eat("小米");
Bird2 bird2 = (Bird2)animal2;
bird2.fly();
}
}
在这个例子中,如果使用 animal2.fly() 调用 fly 的方法就会编译出错,因为 animal 的类型是 Animal, 此时编译器只知道这个类中有一个 eat 方法, 没有 fly 方法。所以如果想用 animal2 调用 fly 方法,就要使用向下转型。
Bird2 bird2 = (Bird2)animal2;
bird2.fly();
但是这样的向下转型有时会出现问题,比如:
Animal2 animal = new Cat2("小猫");
Bird2 bird = (Bird2)animal;
bird.fly();
// 执行结果, 抛出异常
Exception in thread "main" java.lang.ClassCastException: Cat cannot be cast to Bird
at Test.main(Test.java:35)
animal 本质上引用的是一个 Cat 对象, 是不能转成 Bird 对象的。运行时就会抛出异常。
所以,为了让向下转型更加安全,可以先判断 animal 的本质上是否是一个 Bird 实例,再转换。可以使用 instanceof 判断,如果是,返回 true,这时再进行向下转型。
Animal2 animal = new Cat2("小猫");
if (animal instanceof Bird2) {
Bird2 bird = (Bird2)animal;
bird.fly();
} else{
System.out.println("不能向下转型");
}