设计模式原则之一 —— 里氏替换原则

传统继承的优点:
1. 提高代码的重用性,子类拥有父类的方法和属性;
2. 提高代码的可扩展性,子类可形似于父类,但异于父类,保留自我的特性;
缺点:侵入性、不够灵活、高耦合
1. 继承是侵入性的,只要继承就必须拥有父类的所有方法和属性,在一定程度上约束了子类,降低了代码的灵活性;
2. 增加了耦合,当父类的常量、变量或者方法被修改了,需要考虑子类的修改,所以一旦父类有了变动,很可能会造成
非常糟糕的结果,要重构大量的代码。

子类必须实现父类的抽象方法,但不得重写(覆盖)父类的非抽象(已实现)方法。
子类中可以增加自己特有的方法。
当子类覆盖或实现父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松。
当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格。

a.子类必须实现父类的抽象方法,但不得重写(覆盖)父类的非抽象(已实现)方法。

public class A {
public void fun(int a,int b){
System.out.println(a+"+"+b+"="+(a+b));
}
}

public class B extends A{
@Override
public void fun(int a,int b){
System.out.println(a+"-"+b+"="+(a-b));
}
}

public class demo {
public static void main(String[] args){
System.out.println(“父类的运行结果”);
A a=new A();
a.fun(1,2);
//父类存在的地方,可以用子类替代
//子类B替代父类A
System.out.println(“子类替代父类后的运行结果”);
B b=new B();
b.fun(1,2);
}
}

运行结果:
父类的运行结果
1+2=3
子类替代父类后的运行结果
1-2=-1

b.子类中可以增加自己特有的方法。

public class A {
public void fun(int a,int b){
System.out.println(a+"+"+b+"="+(a+b));
}
}

public class B extends A{
public void newFun(){
System.out.println(“这是子类的新方法…”);
}
}

public class demo {
public static void main(String[] args){
System.out.print(“父类的运行结果:”);
A a=new A();
a.fun(1,2);
//父类存在的地方,可以用子类替代
//子类B替代父类A
System.out.print(“子类替代父类后的运行结果:”);
B b=new B();
b.fun(1,2);
//子类B的新方法
b.newFun();
}
}

运行结果:
父类的运行结果:1+2=3
子类替代父类后的运行结果:1+2=3
这是子类的新方法…

c.当子类覆盖或实现父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松。

public class LSP {

 class A {
    public void fun(HashMap map){
        System.out.println("父类被执行...");
    }
}
 
 class B extends A{
    public void fun(Map map){
        System.out.println("子类被执行...");
    }
}
 
 public static void main(String[] args){
        System.out.print("父类的运行结果:");
        LSP lsp =new LSP();
        LSP.A a= lsp.new A();
        HashMap<Object, Object> map=new HashMap<Object, Object>();
        a.fun(map);
        //父类存在的地方,可以用子类替代
        //子类B替代父类A
        System.out.print("子类替代父类后的运行结果:");
        LSP.B b=lsp.new B();
        b.fun(map);
    }

}

运行结果:
父类的运行结果:父类被执行…
子类替代父类后的运行结果:父类被执行…
符合条件
我们应当注意,子类并非重写了父类的方法,而是重载了父类的方法。因为子类和父类的方法的输入参数是不同的。
子类方法的参数Map比父类方法的参数HashMap的范围要大,所以当参数输入为HashMap类型时,只会执行父类的方法,不会执行父类的重载方法。这符合里氏替换原则。

//将子类方法的参数范围缩小会怎样?
import java.util.Map;
public class A {
public void fun(Map map){
System.out.println(“父类被执行…”);
}
}

import java.util.HashMap;
public class B extends A{
public void fun(HashMap map){
System.out.println(“子类被执行…”);
}
}

import java.util.HashMap;

public class demo {
static void main(String[] args){
System.out.print(“父类的运行结果:”);
A a=new A();
HashMap map=new HashMap();
a.fun(map);
//父类存在的地方,都可以用子类替代
//子类B替代父类A
System.out.print(“子类替代父类后的运行结果:”);
B b=new B();
b.fun(map);
}
}

运行结果:
父类的运行结果:父类被执行…
子类替代父类后的运行结果:子类被执行…

在父类方法没有被重写的情况下,子方法被执行了,这样就引起了程序逻辑的混乱。
所以子类中方法的前置条件必须与父类中被覆写的方法的前置条件相同或者更宽松。不符合里式替换

d.当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格。

public class LSP1 {
abstract class A {
public abstract Map fun();
}

 class B extends A{
        @Override
        public HashMap fun(){
            HashMap b=new HashMap();
            b.put("b","子类被执行...");
            return b;
        }
    }
     
     public static void main(String[] args){
            LSP1 lsp =new LSP1();
            LSP1.A a=lsp.new B();
            System.out.println(a.fun());
        }

}

运行结果:
{b=子类被执行…}
若在继承时,子类的方法返回值类型范围比父类的方法返回值类型范围大,在子类重写该方法时编译器会报错。

看上去很不可思议,因为我们会发现在自己编程中常常会违反里氏替换原则,程序照样跑的好好的。所以大家都会产生这样的疑问,假如我非要不遵循里氏替换原则会有什么后果?
后果就是:你写的代码出问题的几率将会大大增加。
https://www.cnblogs.com/o-andy-o/p/10315188.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值