定义:所有引用基类的地方必须能透明地使用其子类的对象。 摘自-《设计模式之禅》
通俗解释:
即父类出现的地方,替换为子类进行调用也不会出现异常。但反之子类出现的地方,用父类却不一定可行,有可能会发生出java.lang.ClassCastException异常,就是所谓的向下转型异常
单一继承原则:
案例:
/**
* 枪支抽象类
*/
public abstract class AbstractGun {
public abstract void shoot();
}
/**
* 继承抽象父类 实现
*/
public class HandGun extends AbstractGun{
@Override
public void shoot() {
System.out.println("手枪射击。。。");
}
}
/**
* 士兵类
*/
public class Soldier {
//枪支属性
private AbstractGun abstractGun;
public AbstractGun getAbstractGun() {
return abstractGun;
}
public void setAbstractGun(AbstractGun abstractGun) {
this.abstractGun = abstractGun;
}
/**
* 射杀敌人
*/
public void killEnemy(){
System.out.println("士兵准备射击。。。。。");
//开枪射击
abstractGun.shoot();
}
}
public class Main {
public static void main(String[] args) {
Soldier soldier = new Soldier();
soldier.setAbstractGun(new HandGun());
soldier.killEnemy();
}
}
在类中调用其他类时务必要使用父类或接口,如果不能使用父类或接口,则说明类的设计已经违背了LSP原则。 - 摘自《设计模式之禅》
以前忽略的一个问题
覆盖或实现父类的方法时输入参数可以被放大
示例:
package com.pro.second.demo3;
import java.util.Collection;
import java.util.HashMap;
public class Son extends Father{
public Collection doSomething(HashMap hashMap){
System.out.println("子类被执行。。。。。。。。。");
return map.values();
}
public void applyRule(double math ,double english , double chinese ) {
System.out.println("调用子类!!!!!!");
double total = math + english + chinese;
if (total > 260) { // 改了这里
System.out.println("总成绩为 : " + total + ",合格");
} else {
System.out.println("总成绩为 : " + total + ",不合格");
}
}
}
package com.pro.second.demo3;
import java.util.Collection;
import java.util.HashMap;
public class Father {
public Collection doSomething(Map map){
System.out.println("父类被执行。。。。。。。。。");
return hashMap.values();
}
public void applyRule(int math ,double english , double chinese ) {
System.out.println("调用父类!!!!!!");
double total = math + english + chinese;
if (total > 200) { // 改了这里
System.out.println("总成绩为 : " + total + ",合格");
} else {
System.out.println("总成绩为 : " + total + ",不合格");
}
}
}
package com.pro.second.demo3;
import java.util.HashMap;
import java.util.Map;
public class Main {
public static void main(String[] args) {
Father f = new Father();
f.applyRule(100,100,0);
Son son = new Son();
son.applyRule(100,100,0);
}
}
输出结果
调用父类!!!!!!
总成绩为 : 200.0未达到了公司的要求
调用父类!!!!!!
总成绩为 : 200.0未达到了公司的要求
可以看出 子类重写了父类的方法,子类方法参数的范围比父类的范围要大,所以此时会默认调用父类
的方法
再调用doSomething()方法试一下
package com.pro.second.demo3;
import java.util.HashMap;
import java.util.Map;
public class Main {
public static void main(String[] args) {
Father f = new Father();
HashMap hashMap = new HashMap();
f.doSomething(hashMap);
Son son = new Son();
son.doSomething(hashMap);
}
}
父类被执行。。。。。。。。。
子类被执行。。。。。。。。。
Process finished with exit code 0
子类调用了自己的方法
父类方法的输入参数是HashMap类型,子
类的输入参数是Map类型,也就是说子类的输入参数类型的范围扩大了,子类代替父类传递
到调用者中,子类的方法永远都不会被执行。这是正确的,如果你想让子类的方法运行,就
必须覆写父类的方法。大家可以这样想,在一个Invoker类中关联了一个父类,调用了一个父
类的方法,子类可以覆写这个方法,也可以重载这个方法,前提是要扩大这个前置条件,就
是输入参数的类型宽于父类的类型覆盖范围。这样说可能比较难理解,我们再反过来想一
下,如果Father类的输入参数类型宽于子类的输入参数类型,会出现什么问题呢?会出现父
类存在的地方,子类就未必可以存在,因为一旦把子类作为参数传入,调用者就很可能进入
子类的方法范畴 -摘自《设计模式之禅》
结论:
子类在没有覆写父类的方法的前提下,子类方法被执行了,这会引起业务逻辑混乱,因为在实际应用中父类一般都是抽象类,子类是实现类,你传递一个这样的实现类就会“歪曲”了父类的意图,引起一堆意想不到的业务逻辑混乱,所以子类中方法的前置条件必须与超类中被覆写的方法的前置条件相同或者更宽松。 -摘自《设计模式之禅》
覆写或实现父类的方法时输出结果可以被缩小
父类的一个方法的返回值是一个类型T,子类的相同方法(重载或覆
写)的返回值为S,那么里氏替换原则就要求S必须小于等于T,也就是说,要么S和T是同一
个类型,要么S是T的子类,为什么呢?分两种情况,如果是覆写,父类和子类的同名方法的
输入参数是相同的,两个方法的范围值S小于等于T,这是覆写的要求,这才是重中之重,子
类覆写父类的方法,天经地义。如果是重载,则要求方法的输入参数类型或数量不相同,在
里氏替换原则要求下,就是子类的输入参数宽于或等于父类的输入参数,也就是说你写的这
个方法是不会被调用的 -摘自《设计模式之禅》