其二:里氏替换原则【Liskov Substitution Principle】
里氏替换法则有两种定义: 正宗的定义:If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2 then S is a subtype of T.
第二个定义,functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it.
定义的通俗解释是:只要父类能出现的地方我子类就可以出现,而且调用子类还不产生任何的错误或异常,调用者可能根本就不需要知道是父类还是子类。但是反过来就不成了,有子类出现的地方,父类未必就能适应。
里氏替换原则包含了4层意思
1.子类必须完全实现父类的方法(保证父类方法在子类中不发生畸变,即最好不要重写父类方法,若必须要重写,必须为父类方法留一个重载方法)。
2.子类可以有自己的个性。 子类可以添加父类所不具有的方法。
3.覆盖或实现父类的方法时输入参数可以被放大。 如重写父类方法时,本来是要求用一个类的对象作为参数。你也可以使用这个类的父类对象作为参数。举例说明如图:
注意,此时的子类的dosomething方法并不是重写而是重载,不在同一个类里的也能重载方法(学到了)。此时编写一个场景类。
根据里氏替换原则,有父类出现的地方,子类就可以出现,重写场景类。
运行结果是一样的,看明白了吗,父类的输入参数是HashMap型,子类的输入参数是 Map 类型,也就是说子类的输入参数类型的范围扩大了,子类代替父类传递到调用类用,子类的方法永远都不回被执行,这是正确的,如果你想让子类的方法运行,你就必须重写父类的方法。
如果父类的参数是Map,子类是HashMap,那么会出现什么问题呢?
场景类中如果是Father f = new Father();那么执行结果还是父类被执行;如果是Son s = new Son();执行结果就会变为子类被执行!
出问题了,子类在没有重写父类方法的情况下,子类方法被执行了,这会导致业务逻辑混乱。在实际代码中,父类一般是抽象类,子类是实现类,你传递这样一个实现类就会歪曲父类的意图,引起逻辑问题,所以<子类中的方法的前置条件必须与超类中被重写或重载的前置条件相同或更为宽松!>
4.重写或实现父类方法时,输出结果可以被缩小。
父类的一个方法返回值是一个类型 T,子类相同方法(重载或重写)返回值为 S,那么里氏替换法则就要求 S 必须小于等于 T,也就是说要么S 和 T 是同一个类型,要么 S 是 T 的子类,为什么呢?分两种情况,如果是重写,方法的输入参数父类子类是相同的,两个方法的范围值 S 小于等于 T,这个是重写的要求,这个才是重中之重,子类重写父类的方法,天经地义;如果是重载,则要求方法的输入参数不相同,在里氏替换法则要求下就是子类的输入参数大于等于父类的输入参数,那就是说你写的这个方法是不会被调用到的,参考上面讲的前置条件。
网友评论