里氏替换原则 Java代码示例

 
里氏替换原则的本质就是为了多态
 
里氏替换原则(LSP)的核心确实是为了实现“安全的多态”。

详细解释

1. 多态的前提

  • 多态(Polymorphism)是面向对象编程的三大特性之一。
  • 多态的实现前提是:父类引用可以指向子类对象,并且调用方法时,实际执行的是子类的实现。

2. 里氏替换原则的作用

  • 里氏替换原则要求:子类对象必须能够替换父类对象,且程序逻辑不出错。
  • 这样,无论父类引用指向哪个子类对象,程序都能正常运行,这就是多态的安全基础。

3. 如果不遵守LSP

  • 如果子类不能完全替代父类(比如重写方法时行为不一致、抛异常等),
  • 那么多态就会失效,程序会出错,面向对象的灵活性和扩展性也会大打折扣。

总结

  • 里氏替换原则的本质,就是保证多态的正确性和安全性。
  • 只有遵守LSP,才能让“父类引用指向子类对象”时,程序行为始终正确,真正发挥多态的威力。
 

## 一、什么是里氏替换原则?

**定义**:  
子类对象必须能够替换父类对象,且程序逻辑不出错。  
也就是说,**父类能用的地方,子类也能用,且行为一致**。

---

## 二、不遵守LSP的坏处(反例)

### 1. 代码示例

```java
// 父类:鸟
class Bird {
    public void fly() {
        System.out.println("鸟在飞");
    }
}

// 子类:鸵鸟
class Ostrich extends Bird {
    @Override
    public void fly() {
        // 鸵鸟不会飞
        throw new UnsupportedOperationException("鸵鸟不会飞!");
    }
}

public class LSPBadDemo {
    public static void letBirdFly(Bird bird) {
        bird.fly();
    }

    public static void main(String[] args) {
        Bird sparrow = new Bird();
        letBirdFly(sparrow); // 正常输出

        Bird ostrich = new Ostrich();
        letBirdFly(ostrich); // 运行时抛异常
    }
}
```

### 2. 坏处分析

- **父类引用指向子类对象时,行为不一致**,导致程序出错。
- 破坏了多态的初衷,**调用者无法安全地使用父类引用**。
- 代码可维护性差,后续扩展容易出bug。
- 违背了“对扩展开放,对修改关闭”的设计原则。

---

## 三、遵守LSP的好处(正例)

### 1. 代码示例

#### 方法一:用接口区分能力

```java
// 飞行接口
interface Flyable {
    void fly();
}

// 鸟类
class Bird {}

// 麻雀会飞
class Sparrow extends Bird implements Flyable {
    @Override
    public void fly() {
        System.out.println("麻雀在飞");
    }
}

// 鸵鸟不会飞
class Ostrich extends Bird {
    // 没有fly方法
}

public class LSPGoodDemo {
    public static void letItFly(Flyable f) {
        f.fly();
    }

    public static void main(String[] args) {
        Flyable sparrow = new Sparrow();
        letItFly(sparrow); // 正常输出

        // Ostrich 不能传给 letItFly,编译期就能发现问题
        // letItFly(new Ostrich()); // 编译报错
    }
}
```

#### 方法二:用组合而非继承

```java
class Bird {
    // 公共属性
}

class FlyAbility {
    public void fly() {
        System.out.println("会飞");
    }
}

class Sparrow extends Bird {
    private FlyAbility flyAbility = new FlyAbility();
    public void fly() {
        flyAbility.fly();
    }
}

class Ostrich extends Bird {
    // 没有fly方法
}
```

### 2. 好处分析

- **父类能用的地方,子类都能用,且行为一致**,多态安全。
- **调用者不用担心子类行为异常**,代码健壮性高。
- **扩展新类型时,不会破坏原有代码**,易于维护和扩展。
- **编译期就能发现问题**,而不是运行时才出错。

---

## 四、总结

| 不遵守LSP(坏处)         | 遵守LSP(好处)           |
|--------------------------|--------------------------|
| 运行时容易出错            | 多态安全,行为一致        |
| 维护困难,扩展易出bug      | 易于维护和扩展            |
| 调用者无法信任父类引用      | 调用者可以放心用父类引用   |
| 破坏面向对象设计原则        | 遵循设计原则,代码健壮     |

---
posted @ 2025-06-15 18:30  予真  阅读(4)  评论(0)    收藏  举报
努力加载评论中...
点击右上角即可分享
微信分享提示