深入理解多态
里氏替换原则
我们知道子类对象可以赋给父类对象,也可以说子类对象可以完全替换父类对象并出现在父类对象可以出现的任何地方,且程序的行为不会发生改变,但反过来父类对象是不能替换子类对象的!比如汽车类对象就不能替换卡车类对象(虽然卡车也是汽车),因为汽车包含的范围比卡车要大,它也可以是私家车!
这种特性就被称为“里氏替换原则(Liskor Substiution Principle)”。
里氏替换原则的应用
里氏替换原则是软件设计应该遵守的重要原则之一,有了里氏替换原则,才使继承复用成为可能,只有当子类可以替换掉父类时软件功能不受影响,父类才能真正被复用,而子类也能在父类的基础上增加新的行为。
is和as操作符的使用
is用于检查对象和指定类型是否兼容。例如要判断员工集合中的一个元素是否是SE对象,就可以使用下面一段代码
if (empls[i] is SE) { //...... }
as主要用于两个对象之间的类型转换。如下所示
SE se=empls[1] as SE;
而且使用as操作符进行类型转化失败时会返回null,而不会报异常!
但是这并不代表不需要异常处理
PM pm=empls[1] as PM;
pm.SayHi();
如果empls【1】转换为PM失败,虽然这一句没有异常,但是下一句SayHI()方法就可能会报错了!
父类类型作为参数
在里氏替换原则中指出子类对象可以代替父类对象,那么在开发程序时我们可以编写以父类类型作为形式参数的方法,在实际调用时传入子类对象,从而实现多态。
程序在运行中自动判断实际参数属于那种子类,调用相应子类的方法,从而完成多态。
小结:用虚方法实现多态的基本步骤
- 子类重写父类的虚方法,其中有两种方式
- 创建父类对象,用子类对象示例化这个父类对象
- 以父类类型作为形式参数,它的子类对象作为实际传入
- 运行时,根据实际创建对象的类型决定执行那个方法。
抽象类和抽象方法
为什么使用抽象类和抽象方法
demo:
public class TrafficTool { public virtual void Run() { Console.WriteLine("车在行驶"); } }
从这个demo中我们不难看出:如果我们实例化一个TrafficTool对象去调用它的Run()方法,实际意义不大,因为交通工具本身是一个宏观的、抽象的概念,不是某一个具体的交通工具。假如我们不希望这个父类被实例化,并且只提供方法的定义,自己不去实现,而让子类实现这些方法,可以使用抽象类和抽象方法来解决!
抽象方法是一个没有实现的方法,通过在定义是增加abstract关键字可以声明抽象方法,其语法如下:
访问修饰符 abstract 返回值类型 方法名();
注意抽象方法没有闭合的{}括号,而是直接跟了一个;分号,也就是说抽象方法不能有方法体。
含有抽象方法的类必然是抽象类,同样我们用abstract关键字来定义抽象类,其语法如下:
访问修饰符 abstract class 类名{}
抽象类和抽象方法的应用
当从一个抽象父类派生一个子类时,子类将继承父类的所有特征,包括它未实现的抽象方法,抽象方法必须在其子类中实现,除非它的子类也是一个抽象类。与子类重写父类的虚方法一样,在子类中也可以使用override关键字来重写父类的抽象方法。其语法如下:
访问修饰符 override 返回值类型 方法名()
虚方法和抽象方法的区别
虚方法 | 抽象方法 |
用virtual修饰 | 用abstract修饰 |
要有方法体即使是一个;号 | 不允许有方法体 |
可以被子类override | 必须被子类override |
除了密封类外定义 | 只能在抽象类中定义 |