理论知识
概念
- 子类必须完全实现父类的抽象方法,且不可重写父类非抽象方法
- 子类可以实现特有方法
- 子类可透明访问父类的所有方法
优点
- 降低继承带来的耦合性的增加
- 子类不重写父类非抽象方法,降低了人为失误导致的代码逻辑错误
- 提高代码可读性,使代码更优雅
代码实现
不遵循里氏替换原则
代码
package com.study.liskov;
public class Liskov1 {
public static void main(String[] args) {
A a = new A();
ExtendA ea = new ExtendA();
System.out.print("输入A: ");
a.info();
System.out.print("再输出A: ");
ea.info();
}
}
class A{
public void info() {
System.out.println("我是A");
}
public void baseMethod1(){
System.out.println("基础方法1");
}
public void baseMethod2() {
System.out.println("基础方法2");
}
}
class ExtendA extends A{
public void info() {
System.out.println("我是extendA");
}
}
分析
两个 info() 的目的都是输入A,但由于没有遵循里氏替换原则,开发者在写A的子类extendA时出现失误,将info() 重写为了输出extendA
遵循里氏替换原则
代码
package com.study.liskov;
public class Liskov2 {
public static void main(String[] args) {
A1 a = new A1();
ExtendA1 ea = new ExtendA1();
System.out.print("输出A: ");
a.info();
System.out.print("再输出A: ");
ea.info();
System.out.print("ExtendA 中使用 A: ");
ea.UseA(a);
System.out.print("参数为BaseA, 传入A: ");
ea.UseBaseA(a);
}
}
abstract class BaseA {
// A类 的基础方法
public void info() {
System.out.println("我是A");
}
public void baseMethod1() {
System.out.println("基础方法1");
}
public void baseMethod2() {
System.out.println("基础方法2");
}
abstract public void run();
}
class A1 extends BaseA {
public void uniqueA() {
System.out.println("A 特有方法");
}
@Override
public void run() {
System.out.println("A -> running......");
}
}
class ExtendA1 extends BaseA {
A1 a = new A1();
@Override
public void run() {
System.out.println("ExtendA -> running......");
}
public void UseA(A1 a){
a.uniqueA();
}
public void UseBaseA(BaseA ba) {
ba.baseMethod1();
}
}
分析
- 将所有基础方法写入BaseA中,在写A和ExtendA时放置了子类重写父类的问题
- 在ExtendA 中使用A, 使用组合的方式
- 由于子类能够透明的完全访问父类, 当需要一个BaseA参数时,可以传入一个A
总结思考
如何理解替换?
参数或变量为BaseA时,可以替换成BaseA的任意一个子类: A或ExtendA, 因为子类覆盖了父类的所有抽象方法, 不会出现代码错误,且子类并未覆盖父类的非抽象方法,不会出现逻辑错误