首先了解LSP原则的概念,以下均建立在一个子类实现一个抽象类的前提下
1.子类型可以增加方法,但不可删
2.子类型需要实现抽象类型中的所有未实现方法
3.子类型中重写的方法必须有相同类型的返回值或者符合协变的返回值
4.子类型中重写的方法必须使用同样类型的参数或者符合逆变的参数
5.子类型中重写的方法不能抛出额外的异常,抛出相同或者符合协变的异常
同时要满足更强的不变量、更弱的前置条件、更强的后置条件。
由于子类是抽象类的实现,所以1和2很好理解。
协变
即子类型的返回值类型与异常类型越来越具体
逆变
即子类型的参数越来越抽象
思考:每个类都会有public方法,有些类会实现interface,供其他类使用,自身就处在一个服务的位置上。 每个public方法都是自身所做出的一个承诺,只要你按照要求调用,就会提供正确的服务。 子类在继承后,固然是获得了父类的带来的‘财富’,更重要的是要遵守父类做出的承诺, 破坏了这个承诺实际上是没有资格继承父类的。
在设计父类与子类时,必须满足LSP原则。
下面给出一个实例:
public abstract class Bird {
private String name;
public void setName(String name){
this.name = name;
}
public void fly() {
System.out.println(name + " fly"); }
}
上面定义了一个Bird类,大多数鸟都会飞。
然而,鸡不会飞,这里抛出Runtime异常
public class Chicken extends Bird {
@Override public void fly() {
throw new RuntimeException();
}
}
而这很明显违反了LSP原则,因为子类抛出了额外的异常。
那么如何解决这种情况呢?这里给出委派的方法,将fly这个动作委派给fly接口实现。
public abstract class Bird {
private String name;
private Flyable f;
public void setFlyable(Flyable f){
this.f = f; }
}
public interface Flyable {
public void fly();
}
这样就不会涉及到继承关系,也不会违反LSP原则。