多态
多态: 一个对象可以指向多种实际类型,同一个事件发生在不同的对象上会产生不同的结果。
向上转型: 通过父类声明一个子类对象,该对象本质类型为子类类型,但被转型为父类类型。
向上转型的性质: 向上转型了的对象只能调用父类中的字段和方法,不能调用子类中的字段和方法。但是可以通过向下转型来调用。
例:
Animal类
public class Animal {
protected String name;
public Animal(String name) {
this.name = name;
}
public void eat() {
System.out.println("小动物在吃饭!");
}
}
Cat类
public class Cat extends Animal {
public Cat(String name) {
super(name);
}
public void run() {
System.out.println("猫咪在跑步!");
}
}
Test类
public class Test {
public static void main(String[] args) {
Animal animal = new Cat("小猫");
//只能调用父类的字段和方法
System.out.println(animal.name); //小猫
animal.eat(); //小动物在吃饭!
//不能调用子类的字段和方法
//animal.run(); 会报错
}
}
- 从上例可以看出 animal 对象只能调用父类中的字段和方法。
向上转型的几种常见情况:
-
直接赋值:
Animal animal = new Cat();
-
方法传参:
public static void func(Animal animal) { //方法体语句 } func(cat);
-
方法的返回值:
public static Animal func() { Cat cat = new Cat(); return cat; }
向下转型:
例:
public class Test {
public static void main(String[] args) {
Animal animal = new Cat("小猫");
//只能调用父类的字段和方法
Cat cat = (Cat) animal;
System.out.println(cat.name); //小猫
cat.eat(); //小动物在吃饭!
cat.run(); //猫咪在跑步!
}
}
- 向下转型后就可以调用子类的字段和方法。
- 向下转型的格式很简单,是一种强制转换。
- 向下转型很危险,因为强制转换的时候程序并不知道转换的类型是不是当前对象的真正类型。
- 可以看出当子类调用 eat 方法时,打印的是小动物在吃饭! ,但是我们现在的对象是 cat ,这显然不符合我们的本意,于是就有了动态绑定。
向下转型的危险之处:
例:
Bird类
public class Bird extends Animal {
public Bird(String name) {
super(name);
}
public void fly() {
System.out.println("小鸟在飞!");
}
}
Test类
//这是错误的代码演示!
public class Test {
public static void main(String[] args) {
Animal animal = new Cat("小猫");
Bird bird = (Bird) animal;
System.out.println(bird.name);
bird.eat();
bird.fly();
}
}
- 很明显 animal 的本质类型是 Cat 类型,将它强制转换为 Bird 类型,却可以通过编译,但是无法运行。
- 于是就有了 instanceof 这个关键字。
instanceof关键字
功能: 判断当前对象的本质类型,返回一个boolean类型的值。
格式:当前对象 instanceof 本质类
现在将上例危险的代码改写:
public class Test {
public static void main(String[] args) {
Animal animal = new Cat("小猫");
if (animal instanceof Bird) {
Bird bird = (Bird) animal;
System.out.println(bird.name);
bird.eat();
bird.fly();
}
}
}
- if 语句中的 instanceof关键字返回 false ,不会运行 if 内部的语句。
- 向下转型时,我们要养成使用 instanceof 关键字的好习惯。
动态绑定: 对象在运行时能够认知自己的本质类型。
例:
Animal类
public class Animal {
protected String name;
public Animal(String name) {
this.name = name;
}
public void eat() {
System.out.println(this.name + "小动物吃饭了!");
}
}
Cat类
public class Cat extends Animal {
public Cat(String name) {
super(name);
}
@Override
public void eat() {
System.out.println("猫咪在吃饭!");
}
}
Test类
public class Test {
public static void main(String[] args) {
Animal animal = new Cat("小猫");
animal.eat(); //猫咪在吃饭!
}
}
- animal的本质类型是 Cat 类型,运行时会认知自己的本质类型,而调用本质类的方法。
- 父类对象调用了父类和子类的同名方法(重写方法),会发生动态绑定。
- 动态绑定也会发生在构造方法中。
动态绑定的前提:
- 向上转型
- 重写父类方法(@Override)
多态的意义: 程序开始的时候有一个确定的父类对象,但是要根据用户的具体选择来实例化成哪一种类型。
例:
Test类
public class Test {
public static void main(String[] args) {
Animal animal = choice();
}
public static Animal choice() {
Scanner scanner = new Scanner(System.in);
while(true){
int choice = scanner.nextInt();
if (choice == 1) {
return new Cat("小猫");
} else if (choice == 2) {
return new Bird("小鸟");
} else {
System.out.println("输入错误,请重新选择!");
}
}
}
}
- 返回一个对象用父类类型接受,再利用动态绑定调用当前对象的方法,非常方便。
- 这里只是简单的演示下使用多态的流程,用户可以根据自己的需要来执行不同的操作。
多态的好处:
- 类的调用者对类的使用成本进一步降低。类的调用者连这个类的类型都不必知道,只需要这个对象具有某个方法即可。
- 能够降低“圈复杂度”,避免了使用大量的 if-else 语句,造成代码冗余。
- 拓展能力强,对类的调用者来说,只要创建一个新类的实例即可。
多态降低圈复杂度的演示:
Shape类
class Shape {
public void draw() {
}
}
Circle类
class Circle extends Shape {
public void draw() {
System.out.println("这是一个圆形!");
}
}
Rectangle类
class Rectangle extends Shape {
public void draw() {
System.out.println("这是一个矩形!");
}
}
Square类
class Square extends Shape {
public void draw() {
System.out.println("这是一个正方形!");
}
}
Triangle类
class Triangle extends Shape {
public void draw() {
System.out.println("这是一个三角形!");
}
}
不利用多态:
Test类
public class Test {
public static void main(String[] args) {
Rectangle rectangle = new Rectangle();
Circle circle = new Circle();
Triangle triangle = new Triangle();
Square square = new Square();
Shape[] shapes = {rectangle, circle, triangle,
circle, rectangle, square};
//如果没有多态
for (Shape shape : shapes) {
if (shape.equals(rectangle)) {
shape.draw();
} else if (shape.equals(circle)) {
shape.draw();
} else if (shape.equals(square)) {
shape.draw();
} else {
shape.draw();
}
}
}
}
利用多态:
Test类
public class Test {
public static void drawMap(Shape shape) {
shape.draw();
}
public static void main(String[] args) {
Rectangle rectangle = new Rectangle();
Circle circle = new Circle();
Triangle triangle = new Triangle();
Square square = new Square();
Shape[] shapes = {rectangle, circle, triangle,
circle, rectangle, square};
//利用多态
for (Shape shape : shapes) {
shape.draw();
}
}
}
运行结果:
这是一个矩形!
这是一个圆形!
这是一个三角形!
这是一个圆形!
这是一个矩形!
这是一个正方形!
- 两种代码的结果相同,但显然,利用了多态不仅代码简洁,也清晰易懂。