Skip to main content

多态

多态概念

开闭原则

程序应该对扩展开放,对修改源代码关闭。

多态:多种形态,多种状态。

同一个事物,由于条件不同,产生的结果也不同。不同游戏中按键功能也不同。例如:一个按键,在开大招的时候,有不同的效果,例如异化师,扇子妈,萨乐芬妮等英雄。

多态的概念

多态:同一个引用类型,使用不同的实例而执行不同操作。

多态的前提

继承和方法重写:子类继承父类,子类重写父类的方法。

向上转型

父类引用指向子类对象属于向上转型,此时通过父类引用可以访问的是子类重写或者继承父类的方法,但不能访问子类独有的方法。

多态向上转型的情况:存在以下任一种就称之为多态。

情形1. 父类作为形参,子类作为实参
情形2. 父类作为声明返回值,实际返回值为子类类型
情形3. 父类类型的数组、集合,元素为子类类型

情形1

1.父类作为形参,子类作为实参

package com.moreStatus;

public class Pet {
protected String name;
protected int health;
protected int love;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getHealth() {
return health;
}

public void setHealth(int health) {
this.health = health;
}

public int getLove() {
return love;
}

public void setLove(int love) {
this.love = love;
}

public Pet(String name, int health, int love) {
this.name = name;
this.health = health;
this.love = love;
}
public Pet() {
}
public void print(){
System.out.println("宠物的名字是:" + name);
System.out.println("宠物的健康值是:" + health);
System.out.println("宠物的亲密值是:" + love);
}

public void cure(){
System.out.println("宠物看病");
}


}

package com.moreStatus;

public class Dog extends Pet{
private String strain;

public String getStrain() {
return strain;
}

public void setStrain(String strain) {
this.strain = strain;
}

public Dog(String name, int health, int love, String strain) {
super(name, health, love);
this.strain = strain;
}

public Dog() {}

// 重写父类看病方法

@Override
public void cure() {
System.out.println("狗狗看病,吃药,吃骨头,健康值恢复");
this.setHealth(100);
}
}

package com.moreStatus;

public class Penguin extends Pet{
private char gender;

public char getGender() {
return gender;
}

public void setGender(char gender) {
this.gender = gender;
}

public Penguin(String name, int health, int love, char gender) {
super(name, health, love);
this.gender = gender;
}

public Penguin() {
}

// 重写父类方法

@Override
public void cure() {
System.out.println("企鹅看病,打针,吃小鱼,健康值恢复");
this.setHealth(100);

}

}

package com.moreStatus;

public class Master {
public void toHospitalWithDog(Dog dog){
dog.cure();
}

public void toHospitalWithPenguin(Penguin penguin){
penguin.cure();
}

// 开闭原则 : 程序应该对扩展开放 对修改源代码关闭
// 问题分析:以上代码编写了两个方法分别用于给不同的宠物子类看病 这种方式不符合开闭原则
// 如果后续有更多的宠物子类 那么还需要编写更多的方法来实现
// 解决方案:使用多态解决 我们应该编写一个方法 实现给所有的宠物子类看病
public void toHospitalWithPet(Pet pet){ // Pet pet = new Dog(); = new Penguin();
pet.cure();
}
}

package com.moreStatus;

public class Test {
public static void main(String[] args) {
Master mas = new Master();
Dog dog1 = new Dog("十一月",99,99,"金毛");
mas.toHospitalWithPet(dog1);
// 打印宠物现在的信息
dog1.print();
}
}

情形2

2.父类作为声明返回值,实际返回值为子类类型

package com.moreStatus;

public class Master {

// 获得 一个 企鹅对象
public Penguin getPenguin(){
Penguin penguin = new Penguin("小白", 100, 100, '雄');
return penguin;
}
// 获得 一个 狗狗对象
public Dog getDog(){
Dog dog = new Dog("大黄", 100, 100, "金毛");
return dog;
}
// 获得 一个 宠物对象
// 使用多态的方式
// 父类作为声明返回值,实际返回值为子类类型
public Pet getPet(String str){
if (str.equals("No1")){
Penguin penguin = new Penguin("小白", 100, 100, '雄');
return penguin;
} else{
Dog dog = new Dog("大黄", 100, 100, "金毛");
return dog;
}
}
}
package com.moreStatus;

public class Test {
public static void main(String[] args) {
Master mas = new Master();

Pet pet = mas.getPet("No1");
System.out.println("获得的奖品是 "+pet.name);
}
}

情形3

3.父类类型的数组、集合,元素为子类类型。

package com.moreStatus;

public class Test {
public static void main(String[] args) {
Pet [] pets = new Pet[3];
pets[0] = new Dog("十一月",98,89,"金毛");
pets[1] = new Dog("十二月",98,89,"金毛");
pets[2] = new Penguin("一月",98,89,'雄');
pets[0].print();
pets[1].print();
pets[2].print();
}
}

多态向上转型的使用事项

父类引用指向子类对象,此时通过父类引用,在对象使用多态的方式调用方法时,可以访问的是子类重写或者继承父类的方法,不能访问子类独有的方法

向下转型

技术背景问题

父类引用指向子类对象属于向上转型,此时通过父类引用,可以访问的是子类重写或者继承父类的方法但不能访问子类独有的方法,如需访问,则必须向下转型。

如需访问子类独有的方法,必须向下转型

多态向下转型的含义和注意

向下转型:

是将指向子类对象的父类引用 转换为 子类类型,而不是 将指向父类对象的父类引用 转换为子类类型

例如:
// 正确的
Pet pet = new Dog();
Dog dog = (Dog) pet;
// 下边的是错误的
Pet pet = new Pet(); // 没有向上转型
Dog dog = (Dog) pet;

总结:必须先向上转型 才可以向下转型 否则将出现类型转换异常 ClassCastException

多态向下转型示例

// 先向上转型
Pet pet = new Dog();

if(pet instanceof Dog){
// pet 就是 子类对象的父类引用对象,将pet再转为Dog子类属性
Dog dog = (Dog)pet;
// 这样就可以使用子类对象中的属性了
dog.playFlyDisc();
}

使用 instanceof 关键字进行类型检查

因为异常会中断程序 所以 在实际开发中我们会使用instanceof关键字 在类型转换之前进行判断 如果类型正确 则转换 不正确 则不转
instanceof 对象类型检查判断
参数:无
返回值:boolean

例子:
对象名 instanceof 类名
Person p1 = new Person()
boolean result = p1 instanceof Dog

多态案例

package com.moreStatus;

/*
* 父类引用指向子类对象属于向上转型,此时通过父类引用,
* 可以访问的是子类重写或者继承父类的方法
* 不能访问子类独有的方法 如需访问 则必须向下转型
*
* 向下转型:
* 是将指向子类对象的父类引用 转换为 子类类型
* 而不是 将指向父类对象的父类引用 转换为子类类型
* 总结:必须先向上转型 才可以向下转型 否则将出现类型转换异常 ClassCastException
*
* 因为异常会中断程序 所以 在实际开发中我们会使用instanceof关键字 在类型转换之前
* 进行判断 如果类型正确 则转换 不正确 则不转
* 用法: 对象名 instanceof 类名
* 表示判断左侧的对象是否属于右侧的类型
*/

public class Test {
public static void main(String[] args) {
// 创建宠物类
Pet pet = new Dog("十一月",98,39,"乌萨齐"); // 向上转型
// 判断类型
if (pet instanceof Dog){ // 判断类型正确, pet 属于 Dog类型
// pet对象强转为dog对象 === 父类引用转为子类对象
Dog dog = (Dog) pet;
dog.playFlyDisc();
}
// 没有向上转型
Pet p1 = new Pet();
if(p1 instanceof Dog){ // 判断类型不正确, pet 不属于 Dog类型
Dog dog1 = (Dog)p1; // 转型错误 有异常 ClassCastException 报错
System.out.println("dog1 = " + dog1);
}else{
System.out.println("类型不匹配");
}
System.out.println("程序结束");
}
}

package com.moreStatus;

public class Pet {
protected String name;
protected int health;
protected int love;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getHealth() {
return health;
}

public void setHealth(int health) {
this.health = health;
}

public int getLove() {
return love;
}

public void setLove(int love) {
this.love = love;
}

public Pet(String name, int health, int love) {
this.name = name;
this.health = health;
this.love = love;
}
public Pet() {
}
public void print(){
System.out.println("宠物的名字是:" + name);
System.out.println("宠物的健康值是:" + health);
System.out.println("宠物的亲密值是:" + love);
}

public void cure(){
System.out.println("宠物看病");
}


}

package com.moreStatus;

public class Dog extends Pet{
private String strain;

public String getStrain() {
return strain;
}

public void setStrain(String strain) {
this.strain = strain;
}

public Dog(String name, int health, int love, String strain) {
super(name, health, love);
this.strain = strain;
}

public Dog() {}

// 重写父类看病方法

@Override
public void cure() {
System.out.println("狗狗看病,吃药,吃骨头,健康值恢复");
this.setHealth(100);
}
// 狗狗玩飞盘方法
public void playFlyDisc(){
System.out.println("狗狗玩飞盘");
}
}

package com.moreStatus;

public class Penguin extends Pet{
private char gender;

public char getGender() {
return gender;
}

public void setGender(char gender) {
this.gender = gender;
}

public Penguin(String name, int health, int love, char gender) {
super(name, health, love);
this.gender = gender;
}

public Penguin() {
}

// 重写父类方法

@Override
public void cure() {
System.out.println("企鹅看病,打针,吃小鱼,健康值恢复");
this.setHealth(100);

}

// 企鹅冲QB方法
public void payQB(){
System.out.println("企鹅冲了100QB");
}

}

多态补充

我们观察重写Object类中的equals方法,父类中的方法实现形参为Object类型,所以我们重写形参也必须为Object类型,但是这样我们通过父类类型的形参就无法访问子类中的属性或者方法,所以我们在方法中必须向下转型。

父类写为Object类型,是为了子类的通用性。
子类在重写父类方法中又向下转型,是为了实用性。

Person类中equals重写,运用多态。

package com.rewrite;

public class Person {
private String name;
private String idCard;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getIdCard() {
return idCard;
}

public void setIdCard(String idCard) {
this.idCard = idCard;
}

public Person(String name, String idCard) {
this.name = name;
this.idCard = idCard;
}

public Person() {
}

public void printInfo(){
System.out.println("我的名字是" + name);
System.out.println("我的id是" + idCard);
}

@Override
// 自定义类的equals方法重写
public boolean equals(Object obj){ // 调用的过程 符合向上转型
// 首先判断地址
if(this == obj){
return true;
}
// 加一层判断,如果类型不相同直接返回false
if (obj instanceof Person){
// 向下转型(强制类型转换),要使用子类对象的属性
Person p1 = (Person) obj;
if (this.name.equals(p1.name) && this.idCard.equals(p1.idCard)){
return true;
}
}
return false;
}

// 自定义类重写哈希值计算
// 要保证在equals比较为true的情况下 两个对象hashCode相同
// 上边重写的 equals 方法是根据人的名字和身份证号比较的
// 所以我们也要根据人的名字和身份证号来计算hash值
@Override
public int hashCode() {
int result = 1; // 哈希值结果
int prime = 31; // 权重
result = result * prime + (this.name == null ? 0 : this.name.hashCode());
// 这里使用了 String类重写的hashCode方法
result = result * prime + (this.idCard == null ? 0 : this.idCard.hashCode());
return result;
}
}

实现原理