Javaのポリモーフィズムの使い道とは?処理を差し替えやすくする考え方

最終更新日

Schoo Java入門 初級 第8回 ポリモーフィズム・コレクション

本記事は、Schooの「Java入門 初級【2026年版】」第8回「ポリモーフィズム・コレクション」を学ぶ前後に、Javaのポリモーフィズムが実務でどのように使われるのかを整理したい方向けのステップアップ学習記事です。

結論からいうと、ポリモーフィズムは「使う側が具体クラスを直接知りすぎないようにする」ために使われます。具体クラスを直接決め打ちしないことで、あとから処理を差し替えやすくなります。

大事なのは、変数を使い回すことでも、必ず Listfor 文と組み合わせることでもありません。呼び出す側がインターフェースの型に対して処理を依頼し、実際に動く具体クラスを外側から差し替えられることがポイントです。

Schoo コースページ:Java入門 初級【2026年版】

PaymentGatewayに入れるコードだけを差し替え、OrderServiceの呼び出しは変わらないことを示す図
PaymentGateway gateway に入れるコードだけを差し替えます。差し替えた後も、OrderService の中では同じ gateway.pay(amount) を呼びます。

実務では処理を差し替えやすくしたい

たとえば、注文処理の中で決済を行う場面を考えます。最初は1つの決済処理だけでよくても、あとから別の決済サービスに変えたり、動作確認用の仮の処理に差し替えたりしたくなることがあります。

そこで、実際の決済クラスと仮の決済クラスを分けます。ただし、注文処理を行う OrderService がそれぞれの具体クラスを直接知りすぎると、差し替えに弱いコードになります。

このとき、PaymentGateway という共通のインターフェースを用意すると、OrderService は「決済できるもの」として扱えます。

図解:使う側はインターフェースだけを見る
使う側
OrderService
決済する処理を呼び出したい
共通の型
PaymentGateway
pay() を呼べるという約束
実際の実装
実際の決済処理
仮の決済処理
どちらを使うかは外側で決める
OrderService は PaymentGateway に依存します。具体的にどの決済処理を使うかは、OrderService の外側で決めます。

共通の型をインターフェースで決める

まず、決済処理に共通する呼び出し方をインターフェースとして決めます。

public interface PaymentGateway {
    boolean pay(int amount);
}

このインターフェースは、「金額を渡すと決済できる」という約束です。実際に外部サービスへ接続するのか、テスト用に成功したことにするのかは、実装クラス側で決めます。

public class ProductionPaymentGateway implements PaymentGateway {
    public boolean pay(int amount) {
        System.out.println("本番の決済サービスへ接続します");
        return true;
    }
}

public class FakePaymentGateway implements PaymentGateway {
    public boolean pay(int amount) {
        System.out.println("テスト用に決済成功として扱います");
        return true;
    }
}

使う側はPaymentGatewayだけを受け取る

注文処理を行うクラスは、具体クラスではなく PaymentGateway を受け取ります。

public class OrderService {
    private final PaymentGateway paymentGateway;

    public OrderService(PaymentGateway paymentGateway) {
        this.paymentGateway = paymentGateway;
    }

    public void order(int amount) {
        boolean paid = paymentGateway.pay(amount);

        if (paid) {
            System.out.println("注文を確定します");
        }
    }
}

OrderService は、ProductionPaymentGateway なのか FakePaymentGateway なのかを知りません。知っているのは、PaymentGateway として pay() を呼べる、ということだけです。

図解:外側から使いたい実装を渡す
本番環境
ProductionPaymentGateway
実際の決済サービスへ接続
テスト環境
FakePaymentGateway
外部サービスに接続せず結果を返す
使う側
gateway.pay(amount);
呼び出し方は変わらない
環境によって実装は変わりますが、使う側のコードは PaymentGateway に対して pay() を呼ぶだけです。

外側から実装を渡すと使う側を変えずに済む

どの決済処理を使うかは、OrderService の外側で決めます。OrderService は、渡された PaymentGateway を使って pay() を呼び出すだけです。

実際の決済処理を使いたいときは ProductionPaymentGateway を渡し、外部サービスを呼ばずに動作確認したいときは FakePaymentGateway を渡します。どちらが渡されても、OrderService のコードは paymentGateway.pay(amount) のままです。

図解:テストでは外部サービスを呼ばない実装に差し替える
実際の処理
決済会社のAPIを呼ぶ
仮の処理
決済成功として返すだけ
OrderService
どちらでも pay() を呼ぶ
テストでは本物の決済を走らせず、同じインターフェースを持つテスト用実装に差し替えます。

なぜこの書き方が実務で役立つのか

この作りにしておくと、動作確認で外部サービスを呼ばずに済みます。決済会社のAPI、メール送信、クラウドストレージ、外部システム連携などは、確認時に本物を呼びたくないことがよくあります。

また、あとから別の決済サービスに変更したい場合も、PaymentGateway を実装した新しいクラスを用意すれば、OrderService 側の変更を抑えやすくなります。

つまり、ポリモーフィズムは「同じメソッド名で動きが変わる」という文法だけの話ではありません。実務では、具体クラスへの依存を弱め、差し替えや確認をしやすくするための考え方として使われます。

Listやfor文は必須ではない

ポリモーフィズムを使うために、Listfor 文は必須ではありません。今回の例でも、中心になっているのは OrderServicePaymentGateway に対して pay() を呼ぶ部分です。

もちろん、複数の処理をまとめて扱う場合に List と組み合わせることもあります。ただし、それはポリモーフィズムの応用例であり、必須条件ではありません。

確認問題

  1. OrderServiceProductionPaymentGateway ではなく PaymentGateway 型で受け取る理由は何ですか。
  2. 実際の決済処理と仮の決済処理を切り替えても、OrderService の呼び出し方が変わらないのはなぜですか。
  3. FakePaymentGateway を用意すると、どのような利点がありますか。

演習後の確認:こちらのページを使って、自分で考えた内容を見直せます。

あわせて読みたい関連記事

まとめ

ポリモーフィズムは、呼び出す側が具体クラスを意識しすぎないようにするための考え方です。インターフェースの型で受け取ることで、実際に使う処理を差し替えやすくなります。Listfor 文は必須ではなく、差し替えや確認をしやすくすることが重要な使い道です。

info

シェアする