多态
-
概念:父类引用指向子类对象,从而产生多种形态。
eg: Animal a = new Dog();
二者具有直接或者间接的继承关系时,父类引用可指向子类对象,形成多态。
父类引用仅可调用父类所声明的属性和方法,不可调用子类独有的属性和方法。
多态的两种应用场景:
场景① 父类类型引用作为方法的形参,实现多态,使方法参数的类型更为宽泛 (该父类的任何一个子类均可作为实参传入)
场景② 使用父类类型作为方法返回值,实现多态,使方法可以可以返回不同的子类对象
public class TestApplyPolymorphic {
public static void main(String[] args) {
// 普通:自身引用指向自身对象
Car car = new Car("小汽车", 45, 160000, "领克02"); // this.brand = "领克02" 为子类独有属性
car.run();
// 多态:父类引用指向子类对象
Vehicle veh = new Car("小汽车", 50, 180000, "领克01");
veh.run(); // 方法覆盖 优先
System.out.println("\n****************多态①:父类类型引用作为【形参】****************");
Bus bus = new Bus("公交车", 60, 1000000, 55);
Bicycle bic = new Bicycle("自行车", 15, 300, "蓝色");
Employee emp = new Employee("工程师007");
emp.goHome(car);
emp.goHome(bus);
emp.goHome(bic);
System.out.println("\n****************多态②:父类类型引用作为【返回值】****************");
Employee emp2 = new Employee("工程师996");
Vehicle myVeh = emp2.buyVehicle(50); // 价格可能由用户任意输入 >> 多态② >> 多态①
emp2.goHome(myVeh);
System.out.println("\n****************多态[向上造型](装箱):父类引用中保存真实子类对象****************");
// 多态:父类引用指向子类对象
Vehicle veh1 = new Car("小汽车", 80, 500000, "BMW X5");
veh1.run();
System.out.println("\n****************多态[向下造型](拆箱):父类引用强转回子类本身类型****************");
// 取决于 line:24 行价格决定此时访问子类独有时是哪个子类
if (myVeh instanceof Car) {
Car myCar = (Car) myVeh;
System.out.println("汽车独有品牌:"+ myCar.brand);
// Bus myBus = (Bus) myVeh; // 运行则会抛出类型转换异常:java.lang.ClassCastException
// System.out.println("公交独有座数:" + myBus.seatNum);
} else if (myVeh instanceof Bus) {
Bus myBus = (Bus) myVeh;
System.out.println("公交独有座数:" + myBus.seatNum);
} else if (myVeh instanceof Bicycle) {
Bicycle myBic = (Bicycle) myVeh;
System.out.println("自行车独颜色:" + myBic.color);
}
}
}
// 员工
class Employee {
String name;
public Employee() { }
public Employee(String name) {
this.name = name;
}
/**
* 多态场景①:父类类型作为方法的【形参】,实现多态 (该父类任一个子类均可作为实参传入)
* @param 父类类型引用
*/
public void goHome(Vehicle veh) {
System.out.print(name + "乘坐: ");
veh.run(); // 父类依然遵循子类的覆盖方法优先,因此会打出来带子类方法的那句输出
}
/**
* 多态场景②:父类类型作为方法【返回值】,实现多态,使方法可以可以返回不同的子类对象
* @return 父类类型引用
*/
public Vehicle buyVehicle(int money) { //money:万
Vehicle veh = null; // 方法返回值,父类引用可存储不同子类对象的地址
if (money >= 100) {
veh = new Bus("公交车", 66, 1100000, 50);
} else if (money >= 30) {
veh = new Car("小汽车", 55, 150000, "领克03");
} else {
veh = new Bicycle("自行车", 20, 500, "黄色");
}
return veh;
}
/*
public void goHome(Bus bus) { // 耦合高(模块与模块之间的关联程度,即耦合高=很松散)
System.out.print(name + "正在乘坐");
bus.run();
}
// 类内方法重载
public void goHome(Bicyle bic) {
System.out.print(name + "正在乘坐");
bic.run();
}
*/
}
// 交通工具
class Vehicle {
String type; // 小汽车,公交车,自行车
int speed;
double price;
public Vehicle() {}
public Vehicle(String type, int speed, double price) {
super();
this.type = type;
this.speed = speed;
this.price = price;
}
public void run() {
System.out.println("一辆价值" + price + "RMB的" + type + "正在以" + speed + "/H速度前进...");
}
}
class Car extends Vehicle {
String brand;
public Car() {
super();
}
public Car(String type, int speed, double price, String brand) {
super(type, speed, price);
this.brand = brand;
}
public void run() {
System.out.println("一辆价值" + price + "RMB的" + brand + "品牌的" + type + "正在以" + speed + "/H速度前进...");
}
}
class Bus extends Vehicle {
int seatNum;
public Bus() {
super();
}
public Bus(String type, int speed, double price, int seatNum) {
super(type, speed, price);
this.seatNum = seatNum;
}
public void run() {
System.out.println("一辆价值" + price + "RMB的" + seatNum + "座的" + type + "正在以" + speed + "/H速度前进...");
}
}
class Bicycle extends Vehicle {
String color;
public Bicycle() {
super();
}
public Bicycle(String type, int speed, double price, String color) {
super(type, speed, price);
this.color = color;
}
public void run() {
System.out.println("一辆价值" + price + "RMB的" + color + "的" + type + "正在以" + speed + "/H速度前进...");
}
}
输出:
一辆价值160000.0RMB的领克02品牌的小汽车正在以45/H速度前进...
一辆价值180000.0RMB的领克01品牌的小汽车正在以50/H速度前进...
****************多态①:父类类型引用作为【形参】****************
工程师007乘坐: 一辆价值160000.0RMB的领克02品牌的小汽车正在以45/H速度前进...
工程师007乘坐: 一辆价值1000000.0RMB的55座的公交车正在以60/H速度前进...
工程师007乘坐: 一辆价值300.0RMB的蓝色的自行车正在以15/H速度前进...
****************多态②:父类类型引用作为【返回值】****************
工程师996乘坐: 一辆价值150000.0RMB的领克03品牌的小汽车正在以55/H速度前进...
****************多态[向上造型](装箱):父类引用中保存真实子类对象****************
一辆价值500000.0RMB的BMW X5品牌的小汽车正在以80/H速度前进...
****************多态[向下造型](拆箱):父类引用强转回子类本身类型****************
汽车独有品牌:领克03
向****上****转型(装箱) - <****多态核心概念****>
父类引用中保存真实子类对象。
eg: Animal a = new Dog(); // 对象层面的自动类型转换
仅可调用父类中所声明的属性和方法(遵循属性遮蔽/方法覆盖原则)
向****下****转型(拆箱)
将父类引用中的真实子类对象,强转回子类本身类型。
eg: Animal a = new Dog();
Dog dog = (Dog)a; // 对象层面的强制类型转换
Cat cat = (Cat)a; // Error: 不是真实子类对象new Dog(),编译OK,运行则会抛出类型转换异常:java.lang.ClassCastException
只有转回子类真实类型,才可调用子类独有的属性和方法。
-
向下转型前应该判断引用中的对象真实类型,保证类型转换的正确性
- 语法: 父类引用 instanceof 类型 // 返回boolean类型结果
/* 从动物对象数组中找到目标动物子类 */
public class TestAnimal {
public static void main(String[] args) {
Animal[] as = new Animal[] {
new Dog("Pluto"),
new Cat("Tom"),
new Dog("Snoopy"),
new Cat("Garfield"),
new Dog("XiaoHei"),
new Cat("XiaoPang"),
};
for (int i = 0; i < as.length; i++) {
System.out.println(as[i].getName());
}
System.out.println("-------------------------------------");
Dog[] dogs = getAllDog(as);
for (int i = 0; i < dogs.length; i++) {
System.out.print(dogs[i].getName() + " ");
}
}
/**
* 返回指定相同子类的对象数组
* @param as 父类动物对象数组
* @return Dog[] 一组Dog对象
*/
public static Dog[] getAllDog(Animal[] as) {
// 0.计数器
int count = 0;
// 1.先数一遍要查找的同类子类对象数量
for (int i = 0; i < as.length; i++) {
// 1.1 对数组中元素的类型进行判断
if (as[i] instanceof Dog) {
// 1.2 每找到一个,计数器自增+1
count++;
}
}
// 2.根据计数器结果,创建适合长度的目标数组
Dog[] dogs = new Dog[count]; // 时间与空间的平衡、效率与安全的平衡
// 3.定义一个目标数组有效元素个数
int size = 0;
// 4.将所有的目标同类子类对象,保存在目标数组中
for (int i = 0; i < as.length; i++) {
// 4.1 判断是否为目标子类类型
if (as[i] instanceof Dog) {
// 4.2 保存子类对象到数组中 (多态-向下转型(拆箱)需强转)
dogs[size++] = (Dog) as[i];
//dogs[--count] = (Dog) as[i]; // 也可以,倒序
}
}
// 5.返回目标数组
return dogs;
}
}
class Animal {
private String name;
public Animal() {
super();
}
public Animal(String name) {
super();
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
class Dog extends Animal {
public Dog() {
super();
}
public Dog(String name) {
super(name);
}
}
class Cat extends Animal {
public Cat() {
super();
}
public Cat(String name) {
super(name);
}
}