多态Polymorphism

向上转换Upcasting

项目进度推进

主要问题

Main 文件中 执行代码 重复

想法

放到一个数组/LinkedList 中,然后使用循环

问题

类型不一致

向上转换

基本原则

我们可以使用父类型的对象变量,接住子类型的对象

1
Animal animal = new Dog();
对对象没有影响

下面两段代码创建的都是 Dog 类型的对象。

只不过 一个用 Animal 类型的变量接住,一个 用 Dog 类型的变量接住

1 2
Animal obj1 = new Dog();Dog obj2 = new Dog();
对比

可以使用任何对象自己的类型,或者对象父类的类型,作为变量类型 接住对象

效果是一样的。

但在调用的时候,会有细微差别,之后介绍。

向上转换的位置

变量赋值
创建时立刻
1
Animal animal = new Dog();
创建之后
1 2
Dog dog = new Dog();Animal animal = dog;
参数传递
传输时
1 2 3 4 5 6 7
public void run() { Dog dog = new Dog(); feed(dog);}public void feed(Animal animal) { ...}
返回值传递
返回前
1 2 3 4 5 6
public void run() { Animal animal = create();}public Animal create() { return new Dog();}
返回后
1 2 3 4 5 6
public void run() { Animal animal = create();}public Dog create() { return new Dog();}

向上转换的收益

谁能接住它

一个类型,可以使用任何一个父类型的变量去接

它能接住谁

一个类型的变量,可以接住任何它的直接/间接子类型

项目进度推进

循环 打印 信息

使用 LinkedList 收集所有 Unit

循环打印 description

变量类型 与 调用保障

类型检查系统

所有的语言对数据都有类型的定义。但对变量不一定有

变量没有类型
1 2
let value = 1;value = "10";
变量有类型
1 2!
int a = 1;a = "10";

什么是变量类型

对现有的代码增加类型备注,从而在编译时就可以提前检测到代码的运行风险

使用前

运行中途崩溃,运行时才会报错

1 2!3 4 5 6 7
function half(value) { return value / 2;} function run() { half("10");}
使用后

编译时不通过,无法运行

1 2 3 4 5 6!7
private void half(int value) { return value / 2;} public void run() { half("10");}

调用保障

针对对象类型,对象变量,保障了调用时一定安全的方法

使用前

运行中途崩溃,运行时才会报错

没有类型检查的语言,对对象调用任何方法,编译时都会认为是正确的,

从而调用时,有一定不成功的风险

1 2!3 4 5 6 7
function feed(obj) { obj.eat();} function run() { feed(new Computer())}
使用后

编译时不通过,无法运行

通过类型标记,java 可以检验传输的类型(Computer),是否能够保障提供 标记的类型(Animal) 里所提到的所有的方法,

从而之后可以可以安心的调用方法

1 2 3 4 5 6!7
private void feed(Animal animal) { animal.eat();} public void run() { feed(new Computer());}

调用保障分析

正常情况
1 2 3 4 5 6 7
private void feed(Dog dog) { dog.eat();} public void run() { feed(new Dog());}
向上转换
1 2 3 4 5 6 7
private void feed(Animal animal) { animal.eat();} public void run() { feed(new Dog());}
其它情况
1 2 3 4 5 6!7
private void feed(Animal animal) { animal.eat();} public void run() { feed(new Computer());}

降低访问权限

向上转换

子类型可以保障有所有父类型的方法

1
Animal animal = new Dog();
降低权限

因为减少了保障,接下来的调用里就不能调用在父类里没有的方法

1 2 3!
Animal animal = new Dog();animal.eat();animal.think();
但更多可能

虽然降低了权限,但是变量可以容纳更多可能

1*2
Animal animal = new Person();animal.eat();

核心思维

联合

向上转换、减少保障、降低权限、更多可能

心态

向上转换的原因是,当前的需求并不关心具体是什么类型

项目进度推进

Map

将作战单位加入地图

向下转换Downcasting

项目进度推进

选中

使用矩形选中队员

群体移动 与 群体攻击

希望能让选中的人都能移动到某个位置

恢复访问权限

改变变量类型

希望恢复子类的变量类型,从而可以调用子类的方法

1 2 3
Animal animal = new Dog();Dog dog = animal;dog.bark();
编译时检查

Java 在编译时,会检查这种变量类型转换。并会报错。

因为 java 不能推断出这是安全的。

1 2!3 4
public void feed(Animal animal) { Dog dog = animal; dog.bark();}
强制类型转换

增加强制类型转换,确认你就要这么做

1 2 3
Animal animal = new Dog();Dog dog = (Dog)animal;dog.bark();
联合

向下转换、增加保障、提高权限

向下转换

基本原则

一个 A 类型的 变量,可以被 A 的子类型的变量接住,但需要类型转换

不会对对象有任何影响

1 2
Animal animal = new Dog();Dog dog = (Dog)animal;
可以往哪转

可以转成 对象类型 或者 对象类型的父类

向下转换的位置

变量传输
赋值时
1 2 3
Dog dog = new Dog();Animal animal = dog;Dog dog2 = (Dog)animal;
参数传递
传输前
1 2 3 4 5 6 7
public void run() { Animal animal = new Dog(); feed((Dog)animal));}public void feed(Dog dog) { ...}
传输后
1 2 3 4 5 6 7
public void run() { Dog dog = new Dog(); feed(dog);}public void feed(Animal animal) { Dog dog = (Dog)animal;}
返回值传递
传输后
1 2 3 4 5 6
public void run() { Dog dog = (Dog)create();}public Animal create() { return new Dog();}

类型转换

向上

没问题

1
Animal animal = new Dog();
向下

需要强制类型转换

1 2
Animal animal = new Dog();Dog dog = (Dog)animal;

否则编译时会报错

1 2!
Animal animal = new Dog();Dog dog = animal;
乱来

不可以,转到没有继承关系的类型,写强转也没用

编译时会报错

1 2!
Animal animal = new Dog();Point point = (Point)animal;
过度向下

只要是 强转 x 原变量的子类,编译就过了

但运行时,仍然会检测 对象的类 是不是 新对象变量的类 或者 新对象变量的类 的子类

如果不是,运行时会报错

1 2!3
Animal animal = new Dog();Person person = (Person)animal;person.think();

类型检测

类型检测

需求

某些情况下,无法断定原来是什么类型

1 2 3 4 5!6 7 8
public class Zoo{ private LinkedList<Animal> animals = new LinkedList<>(); public void func() { for (Animal animal : animals) { Dog dog = (Dog)animal; } }}
精准检测

检测 obj 对象的类 是不是 Cla

1 2 3
if (obj.getClass() == Cla.class) { ...}
保障检测

检测 obj 对象的类 是不是 Cla 或者 Cla 的子类

如果是,就代表 Cla 里有的方法, obj 都能安全调用

1 2 3
if (obj instanceof Cla) { ...}

类型检测 x 向下转换

先检测 是否 安全

再转型 恢复权限

1 2 3 4
if (obj instanceof Cla) { Cla obj2 = (Cla)obj; obj2.callSomeFunctionOnlyInCla();}
案例
1 2 3 4 5 6 7 8 9 10 11
public class Zoo{ private LinkedList<Animal> animals = new LinkedList<>(); public void func() { for (Animal animal : animals) { if (animal instanceof Dog) { Dog dog = (Dog)animal; dog.bark(); } } }}

项目进度推进

群体移动 与 群体攻击

恢复访问权限,调用代码

方法覆盖Method Overriding

方法调用实质

能否调用

对象变量类型(左边),决定是否可以安全的调用

调用效果

对象类型(右边,运行时),决定调用效果

决定从哪个类,看是寻找要调用的代码

逐级查找

每次调用时 会从当前 对象 开始查找 有没有被调用的方法

如果没有 则查询父类

直到找到为止

动态绑定Dynamic Binding

调用的方法得在运行时,才能够知道

1 2 3 4 5 6 7
public void run() { feed(new Person()); } public void feed(Animal animal) { animal.eat();}

方法覆盖

什么是

子类写一个 跟父类方法签名一样的方法 的实现

方法签名: 方法名 + 参数类型列表

效果

子类可以再实现一次这个方法

此时再调用时 就不会调用父类的方法

super

什么是

一个关键字

可以用来在子类里 找到父类的代码

语法
super.方法名();
效果

可以继续从父类寻找方法并调用

继承 x 多态Inheritance x Polymorphism

多态现象

对同样的对象变量类型 调用 同样的方法

却执行了不一样的代码

多态思维

类型不确定性

调用方

不能确定 到底调的是具体哪个类

也不需要 知道是哪个类

效果不确定性

调用方

不能确定 到底调的是具体哪个方法

也不需要 知道是哪个方法

项目进度推进

群体移动

使用低访问权限,调用子类代码

Object

规则

如果一个类没有继承任何类,那么这个类继承 Object

对象变量

Object 作为对象变量,可以接住任何类型

toString

Object 有写 toString 方法。

你可以覆盖,覆盖后,println 可以直接传输对象,println 内部会调用 toString 方法

项目进度推进

使用 toString

消除 description

总结

继承的效果

1.

代码重用

2.

构建低权限组

3.

多态

继承 vs 组合

含义

组合:有

继承:是

数量

组合:可以组合多个

继承:只能继承 1 个

功能

组合:代码重用

继承:代码重用 + 构建低权限组 + 多态

额外规则

一个类只能继承一个类

ZZAX 微信公众

文档一更新,立刻告诉你