大家好,欢迎来到IT知识分享网。
一、什么是里氏替换原则
只要能使用父类的地方,都可以用子类替换,反之则不然。它包含如下四个方面的含义:
1、子类必须实现父类的抽象方法,但是不能覆盖父类的非抽象方法
子类如果不实现父类的抽象方法,编译都通不过,这点没有问题,关键是不能覆盖父类的非抽象方法。
2、子类可以增加自己特有的方法
子类如果没有自己特有的方法,跟父类没啥区别,这点不需要解释。
3、当子类重载父类的方法时,方法的形参要比父类更宽松
如果重载的时候,参数不注意,等同于覆盖,这点很关键。
4、当子类重载父类的方法时,方法的返回值要比父类更严格
如果不这样,代码将会编译出错。
上面4点,关键的是1和3,其他的都容易理解。
二、为什么要遵守里氏替换原则
- 确保合理的使用继承
滥用继承将会带来严重后果。继承是为了代码的复用,有优点也有缺点,优点是提高了代码的可复用性和可扩展性,缺点是打破了代码的封装性,使子类和父类互相耦合,所以要用里氏替换原则规范继承的使用,扬长避短。
- 确保使用继承的代码能够满足开闭原则
满足里氏替换原则的代码,一定也满足开闭原则的要求。
三、如何遵守里氏替换原则
- 合理的抽象,确定对象关系是“Is-A”,而不是“Has-A”
例如鸟这个对象,从生物学的角度分析,鹦鹉和鸵鸟都是鸟,但是从类的继承关系看,鹦鹉可以是鸟的子类,但是鸵鸟不能是鸟的子类,因为它不能飞。飞机能飞,但是它也不可能是鸟的子类。Is-A可以是继承关系,而Has-A只能是组合关系。
- 子类继承父类时,可以新增方法,不能覆盖父类的方法
如果覆盖父类的方法,可能会带来错误的结果,例如有如下父类:
父类:SuperType
在构造器中调用了可被覆盖的方法,有如下子类去继承它:
子类:SubType带覆盖方法
执行该代码的时候,将会报错,因为父类的构造器方法先于List对象实例化执行,而父类的构造器又调用了子类的覆盖方法,这时候List还未被初始化,所以抛出了空指针异常。
如果遵守里氏替换原则,将会避免这样错误,同时也要注意:父类的构造器不能调用可被覆盖的方法。
- 当子类重载父类的方法时,方法的形参要比父类更宽松,否则重载会变覆盖
看如下代码,子类重载了父类的方法,但是由于重载不当,起到了覆盖的作用,违背了里氏替换原则:
父类:SuperType
子类:SubType带重载方法
执行方法
执行的结果是运行了子类的方法,子类并没有覆盖父类的方法,只是重载了父类的方法,但是由于子类重载方法的参数ArrayList比父类方法的参数List范围上要更狭窄,所以起到了覆盖的作用。这违反了里氏替换原则。
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/154225.html