里氏替换原则(LSP)描述了,子类可以扩展父类的功能,但是不能改变父类原有的功能
优点:
- 里氏替换时实现开闭原则的重要方式之一
- 避免了重写父类造成了可复用性变差的缺点
- 类的扩展不会对已有系统引入新的异常,降低代码出错的可能
- 加强了程序的健壮性
实现方式:最直接的应用场景就是子类在继承父类的时候,除了完成扩展功能之外尽量避免重写父类的方法
反例:枪可以射击和杀人,那么玩具枪呢?
public static void main(String[] args) {
// 手枪射击并杀人
Gun handgun = new HandGun();
handgun.shoot();// 手枪射击
handgun.kill(); // 手枪杀人
// 玩具枪射击并杀人
Gun toyGun = new ToyGun();
toyGun.shoot(); // 玩具枪射击
toyGun.kill(); // java.lang.RuntimeException: 玩具枪不能杀人!
}
// 基类
interface Gun{
// 射击
void shoot();
// 杀人
void kill();
}
// 手枪
static class HandGun implements Gun{
@Override
public void shoot() {
System.out.println("手枪射击");
}
@Override
public void kill() {
System.out.println("手枪杀人");
}
}
// 玩具枪
static class ToyGun implements Gun{
@Override
public void shoot() {
System.out.println("玩具枪射击");
}
@Override
public void kill() {
throw new RuntimeException("玩具枪不能杀人!");
}
}
正例:针对以上问题,应该再抽象一个只会枪的通用功能的基类【射击】,如下:
public static void main(String[] args) {
// 手枪射击杀人
Gun handGun = new HandGun();
handGun.shoot(); // 手枪射击
handGun.kill(); // 手枪杀人
// 玩具枪射击
AbstractGun toyGun = new ToyGun();
toyGun.shoot(); // 玩具枪射击
}
// 抽象基类,只具备射击功能
interface AbstractGun{
// 射击
void shoot();
}
// 基类,扩展了杀人功能
interface Gun extends AbstractGun{
// 射击
void kill();
}
// 手枪
static class HandGun implements Gun {
@Override
public void shoot() {
System.out.println("手枪射击");
}
@Override
public void kill() {
System.out.println("手枪杀人");
}
}
// 玩具枪
static class ToyGun implements AbstractGun {
@Override
public void shoot() {
System.out.println("玩具枪射击");
}
}