ポリモフィズム(多態性)とは?読み方・種類・Javaの実装をわかりやすく解説
ポリモフィズムは、同じ呼び出し方で、対象となるオブジェクトに応じて異なる動作をさせる仕組みです。日本語では多態性や多相性と訳され、カプセル化・継承・抽象化と並ぶオブジェクト指向の四大原則の一つに数えられます。読み方は「ポリモーフィズム」で、表記ゆれが多い用語でもあります。この記事では、定義と読み方、静的・動的の種類、Javaでの実装、メリットと注意点までをコード例で整理します。
目次
まとめ:ポリモフィズムの要点
ポリモフィズムは「1つの呼び出しが対象次第で違う振る舞いをする」性質で、和訳は多態性(多相性)です。種類は2つあり、引数の型や数で呼び分ける静的ポリモーフィズム(オーバーロード、コンパイル時に解決)と、親クラスの型で受けて実体に応じた処理を呼ぶ動的ポリモーフィズム(オーバーライド、実行時に解決)に分かれます。Javaでは継承やインターフェースのオーバーライドで動的ポリモーフィズムを実現します。最大の効果は、型ごとのif分岐を書かずに新しい型を追加できることで、コードの拡張に強くなります。これはSOLID原則のオープンクローズドの原則や、StrategyパターンなどのデザインパターンのSの土台でもあります。以下で定義から順に見ていきます。
ポリモフィズムとは:定義と読み方・別名
言葉の由来はギリシャ語で「多くの(poly)形(morph)」を意味します。プログラミングでは、同じインターフェース(メソッド名)に対して、複数の異なる実装を切り替えられる性質を指します。
ポリモフィズムの定義(オブジェクト指向の4原則の一つ)
ポリモフィズムは、オブジェクト指向プログラミングの4つの基本原則のうちの一つです。残りはカプセル化、継承、抽象化です。たとえば「鳴く」というメソッドを犬と猫が持つとき、同じ「鳴け」という指示に対して犬は「ワン」、猫は「ニャー」と返します。呼び出す側は相手が犬か猫かを意識せず、同じ書き方で扱えます。この「呼び出し側を変えずに、振る舞いだけを差し替えられる」点が本質です。カプセル化との役割分担はカプセル化とポリモーフィズムの継承との違いで詳しく整理できます。
読み方と別名(ポリモーフィズム・多態性・多相性)
読み方は「ポリモーフィズム」です。「ポリモフィズム」とも表記され、どちらも英語polymorphismの音写で意味は同じです。日本語訳としては多態性が最もよく使われ、文献によっては多相性とも呼ばれます。表記ゆれや読み方が問われやすい用語ですが、いずれも同一の概念を指します。社内ドキュメントや書籍ごとに表記が揺れても、指す内容は変わりません。
ポリモフィズムの種類:静的と動的
ポリモフィズムは、いつ呼び出し先が決まるかで2種類に分かれます。違いを整理します。
| 種類 | 実現方法 | 解決タイミング | 別名 |
|---|---|---|---|
| 静的ポリモーフィズム | オーバーロード | コンパイル時 | 静的束縛 |
| 動的ポリモーフィズム | オーバーライド | 実行時 | 動的束縛 |
静的ポリモーフィズム(オーバーロード)
静的ポリモーフィズムは、同じ名前のメソッドを引数の型や数で複数定義する「オーバーロード」で実現します。どのメソッドを呼ぶかはコンパイル時に決まります。
class Calc {
int add(int a, int b) { return a + b; }
double add(double a, double b) { return a + b; }
}
同じaddでも、整数を渡せば整数版が、小数を渡せば小数版が選ばれます。呼び出し側は型を意識せず同じメソッド名で書ける一方、対応はコンパイル時に固定されます。
動的ポリモーフィズム(オーバーライド)
動的ポリモーフィズムは、親クラスやインターフェースで定義したメソッドを子クラスで上書きする「オーバーライド」で実現します。どの実装が動くかは、変数の宣言型ではなく実行時の実体で決まります。オブジェクト指向で単にポリモーフィズムと言う場合は、通常こちらを指します。次に挙げるJavaの実装も、この動的ポリモーフィズムが中心です。
Javaでのポリモフィズムの実装(継承とインターフェース)
Javaで動的ポリモーフィズムを実現する経路は2つあります。クラスを継承してメソッドをオーバーライドする方法と、インターフェースをimplementsして実装する方法です。
継承とオーバーライドによる実装
親クラスのメソッドを子クラスで上書きし、親クラス型の変数で子インスタンスを扱います。変数の型は親でも、実行されるのは子の処理です。これにより、扱う側のコードを変えずに振る舞いを差し替えられます。共通の基底クラスがある場合に向く方法です。
インターフェースとimplementsによる実装
インターフェースは「実装すべきメソッドの約束」を定義し、各クラスがimplementsで中身を与えます。Javaのimplementsは、このインターフェースの契約を満たすという宣言です。
interface Shape {
double area();
}
class Circle implements Shape {
public double area() { return 3.14 * r * r; }
}
class Square implements Shape {
public double area() { return s * s; }
}
Shape shape = new Circle();
System.out.println(shape.area());
Shape型で受けておけば、中身が円でも四角でもarea()を同じ書き方で呼べます。新しい図形を足すときも、Shapeを実装したクラスを追加するだけで、呼び出し側には手を入れません。継承が「is-aの親子関係」を前提にするのに対し、インターフェースは継承関係のないクラス同士にも共通の振る舞いを持たせられる点が違いです。
ポリモフィズムを使うメリットと注意点
最大のメリットは、型ごとの条件分岐を書かずに済むことです。「もし円なら…、もし四角なら…」というifやswitchの羅列は、種類が増えるたびに全箇所へ追記が必要になります。ポリモーフィズムなら、新しい型はクラスを1つ追加するだけで済み、既存コードを触らずに機能を拡張できます。これがSOLID原則のオープンクローズドの原則(拡張に開き、修正に閉じる)を支える仕組みであり、Strategyパターンをはじめ多くのデザインパターンの基盤になっています。結果として、再利用性と保守性が上がり、変更の影響範囲が狭まります。
一方で、使いどころを誤ると逆効果です。種類が2〜3個で今後も増えないなら、わざわざ抽象化せず単純な分岐のほうが読みやすい場面があります。過剰な抽象化は、処理を追うときにクラス間を行き来させ、かえって理解しにくくします。継承を深く重ねると、どこでメソッドが上書きされたか追いにくくなる問題もあります。「将来、型が増える見込みがあるか」を基準に、増えるならポリモーフィズム、増えないなら素直な分岐、と判断するのが実務的です。
よくある質問
ポリモフィズムの読み方は何ですか?
「ポリモーフィズム」と読みます。英語のpolymorphismをカタカナにしたもので、「ポリモフィズム」と短く書かれることもありますが、どちらも同じ意味です。日本語では多態性、または多相性と訳されます。書籍やドキュメントによって表記が揺れますが、指している概念は同一です。
ポリモフィズムとポリモーフィズムは違うものですか?
同じものです。どちらも英語polymorphismの音写で、長音符の有無による表記の違いにすぎません。検索でも両方の表記が使われますが、内容に差はありません。和訳の多態性・多相性も同じ概念を指します。社内で表記を統一したい場合は、どちらか一方に揃えておくと混乱を避けられます。
オーバーロードとオーバーライドの違いは何ですか?
オーバーロードは同じ名前のメソッドを引数の型や数で複数定義することで、どれを呼ぶかはコンパイル時に決まります(静的ポリモーフィズム)。オーバーライドは親で定義したメソッドを子で上書きすることで、どの実装が動くかは実行時の実体で決まります(動的ポリモーフィズム)。名前は似ていますが、解決のタイミングと目的が異なります。
ポリモフィズムとカプセル化・継承の違いは何ですか?
いずれもオブジェクト指向の原則ですが役割が異なります。カプセル化はデータと操作をまとめ外部から内部を隠す仕組み、継承は既存クラスの性質を引き継いで再利用する仕組み、ポリモーフィズムは同じ呼び出しで異なる振る舞いを実現する仕組みです。ポリモーフィズムは継承やインターフェースを土台に成り立つため、継承と組み合わせて使われることが多くなります。詳しくはカプセル化とは何かも参照してください。
Java以外の言語でもポリモフィズムは使えますか?
使えます。ポリモーフィズムはオブジェクト指向に共通する概念で、C++、Python、C#、Rubyなど多くの言語が備えています。実現方法は言語ごとに異なり、たとえばPythonは型を明示せず実行時にメソッドを解決するダックタイピングで動的ポリモーフィズムを実現します。Javaではインターフェースや継承とオーバーライドで実現する点が特徴です。概念自体は言語に依存しません。