【Java初心者向け】SOLID原則の「L(リスコフの置換原則)」をわかりやすく解説!

こんにちは。ゆうせいです。
今回はSOLID原則の「L:リスコフの置換原則(Liskov Substitution Principle)」について解説します。
一見すると難しそうな名前ですが、内容はとてもシンプルです。Javaのコードを交えて、新人エンジニアでも理解できるよう丁寧に説明していきます。


リスコフの置換原則(LSP)とは?

一言で説明すると?

親クラスのインスタンスを子クラスで置き換えても、プログラムの正しさが保たれるべきという原則です。

もっとやさしく言うと?

お父さんの代わりに息子が出てきても問題ないように作ろう」ということです。
Javaのようなオブジェクト指向プログラミングでは、「継承」が使われますよね。
でも、「継承しているから安心」ではないんです!


悪い例:LSPに違反したコード

まずは基底クラス

public class Bird {
    public void fly() {
        System.out.println("空を飛んでいます!");
    }
}

それを継承した子クラス(違反!)

public class Ostrich extends Bird {
    @Override
    public void fly() {
        throw new UnsupportedOperationException("ダチョウは飛べません!");
    }
}

呼び出し側のコード

public class BirdWatcher {
    public void observe(Bird bird) {
        bird.fly();
    }
}

このとき、BirdWatcher は「どの Bird も飛べる」と信じて fly() を呼びます。
ところが Ostrich を渡すと 例外が発生してしまいます。

これは親の代わりに子を使ったことで動作がおかしくなる=LSP違反なんです!


良い例:LSPを守った設計

「鳥だからといって全部が飛ぶとは限らない」と気づいたとき、設計を見直す必要があります。

インターフェースで行動ごとに分類

public interface Bird {
    void eat();
}

public interface Flyable {
    void fly();
}

クラスごとに責任を分ける

public class Sparrow implements Bird, Flyable {
    public void eat() {
        System.out.println("スズメがご飯を食べています");
    }

    public void fly() {
        System.out.println("スズメが空を飛んでいます");
    }
}

public class Ostrich implements Bird {
    public void eat() {
        System.out.println("ダチョウがご飯を食べています");
    }
}

呼び出し側は型を意識して使い分ける

public class BirdWatcher {
    public void observeEating(Bird bird) {
        bird.eat();
    }

    public void observeFlying(Flyable flyer) {
        flyer.fly();
    }
}

これで、飛べない鳥を飛ばそうとしてエラーになることはありません


LSPを守るためのチェックリスト

チェック項目内容
すべてのサブクラスで、親クラスのメソッドは意味を持っているか?無効なオーバーライドはないか?
サブクラスが親クラスの振る舞いを変えていないか?たとえば順序や制約を壊していないか?
「is-a関係」が本当に成り立っているか?それって本当に「〇〇の一種」?

数学的に理解してみよう!

リスコフの置換原則の定義は次のような形でも表されます:

型Tのオブジェクトが期待されている場所で、型Sのオブジェクト(SはTのサブ型)を使っても動作が破綻しないこと。

つまり:

  • 良い例: S ⊆ T かつ「SはTの契約を守っている」
  • 悪い例: ST のサブクラスでも T の振る舞いを壊している

メリットとデメリット

メリットデメリット
安心して継承を使える設計に気を遣う必要がある
テストや保守がしやすくなる時に継承ではなく委譲の方が適していることも
抽象度が高く、柔軟な設計が可能クラス構造が複雑になる場合も

まとめと次のステップ

リスコフの置換原則は、「継承したら終わり」ではなく、「本当に正しく置き換えられるか?」を意識させてくれる大切な原則です。

新人エンジニアが意識すべきポイント

  • 子クラスで親クラスの振る舞いを変えすぎない
  • 「is-a(〜の一種)」が成り立っているかをよく考える
  • 無理な継承よりも、インターフェース+委譲(composition) を検討しよう!

セイ・コンサルティング・グループの新人エンジニア研修のメニューへのリンク

投稿者プロフィール

山崎講師
山崎講師代表取締役
セイ・コンサルティング・グループ株式会社代表取締役。
岐阜県出身。
2000年創業、2004年会社設立。
IT企業向け人材育成研修歴業界歴20年以上。
すべての無駄を省いた費用対効果の高い「筋肉質」な研修を提供します!
この記事に間違い等ありましたらぜひお知らせください。