オブジェクト指向設計においてよく比較される抽象クラスとインターフェースですが、その違いと適切な使い分けを理解することは設計の質を大きく左右します。
両者は「実装を強制する」という点では共通していますが、持てる機能・継承の制約・設計上の役割が異なるため、正確な理解が必要です。
本記事では抽象クラスとインターフェースの違い・使い分け・各言語での実装について詳しく解説していきます。
抽象クラスとインターフェースの違い:基本的な結論
それではまず、抽象クラスとインターフェースの根本的な違いについて解説していきます。
最も重要な違いは、抽象クラスは実装(具象メソッドとフィールド)を持てるのに対し、インターフェースは基本的に実装を持たず契約(メソッドのシグネチャ)のみを定義する点です。
抽象クラスとインターフェースの比較表
| 比較項目 | 抽象クラス | インターフェース |
|---|---|---|
| 実装の有無 | 具象メソッドを持てる | 基本的に実装なし(defaultメソッドは例外) |
| フィールド | インスタンス変数を持てる | 定数のみ(通常) |
| 継承 | 単一継承のみ(多くの言語) | 複数実装が可能(多重実装) |
| コンストラクタ | 定義できる | 定義できない |
| アクセス修飾子 | 各種使用可能 | publicのみが一般的 |
| 関係性 | is-a関係(継承) | can-do関係(実装) |
インターフェースの役割:契約としての設計
インターフェースは「何ができるか(can-do)」を定義する契約として機能します。
例えばJavaの「Serializable」インターフェースは「このクラスはシリアライズできる」という能力を宣言しており、実装の詳細はクラスが提供します。
インターフェースによって型の多様性(ポリモーフィズム)を実現しつつ、実装の詳細を隠蔽することがオブジェクト指向設計の重要な原則です。
多重実装と多重継承の違い
JavaやC#では多重継承(複数のクラスを継承すること)は禁止されていますが、インターフェースは複数実装(複数のインターフェースを実装すること)が許可されています。
例えば「Flyable(飛べる)」「Swimmable(泳げる)」という2つのインターフェースを一つのクラスが同時に実装することができます。
この多重実装の柔軟性がインターフェースの最大の設計上の強みのひとつです。
抽象クラスとインターフェースの使い分け
続いては、抽象クラスとインターフェースをどのように使い分けるかを確認していきます。
抽象クラスを選ぶべきシーン
抽象クラスが適しているのは、複数のサブクラスに共通の実装(フィールドや具象メソッド)を提供したい場合です。
「is-a関係(〜は〜の一種である)」が明確に成立する場合、例えばCat・Dog・Birdがいずれも「Animal(動物)の一種である」という関係が成立する場合は抽象クラスが自然な選択です。
コードの重複を共通の具象メソッドとして抽象クラスにまとめたい場合は抽象クラスを優先します。
インターフェースを選ぶべきシーン
インターフェースが適しているのは「can-do関係(〜ができる)」を表現したい場合です。
異なる継承階層に属するクラスが共通の能力を持つことを表現する場合、例えばRobotもBirdも「Flyable(飛べる)」という能力を持つが同じ継承ツリーには属さないという状況ではインターフェースが適切です。
また、依存性逆転の原則(DIP)を適用して抽象に依存させたい場合はインターフェースが推奨されます。
Javaのdefaultメソッドとインターフェースの進化
Java 8以降ではインターフェースに「defaultメソッド」として具体的な実装を持つメソッドを定義できるようになりました。
これにより抽象クラスとインターフェースの境界が以前より曖昧になっていますが、インターフェースは依然として「契約」としての役割が主であり、共通の実装を大量に持たせる場合は抽象クラスの方が設計の意図として明確です。
各言語での実装方法と注意点
続いては、代表的なプログラミング言語での抽象クラスとインターフェースの実装方法を確認していきます。
Javaでの実装
Javaでは「abstract class」で抽象クラスを、「interface」でインターフェースを定義します。
クラスは一つの抽象クラスしか継承できませんが、複数のインターフェースを実装できます。
Javaでのインターフェース実装例
interface Flyable { void fly(); }
interface Swimmable { void swim(); }
class Duck extends Animal implements Flyable, Swimmable {
public void fly() { System.out.println(“飛ぶ”); }
public void swim() { System.out.println(“泳ぐ”); }
}
C#での実装
C#もJavaと同様に「abstract class」と「interface」を使い分けます。
C# 8.0以降ではインターフェースのデフォルト実装もサポートされています。
C#ではプロパティをインターフェースに定義できる点がJavaと異なる特徴のひとつです。
| 言語 | 抽象クラスのキーワード | インターフェースのキーワード | 多重継承 |
|---|---|---|---|
| Java | abstract class | interface | インターフェースのみ複数可 |
| C# | abstract class | interface | インターフェースのみ複数可 |
| Python | ABC + abstractmethod | ABC(慣習的に使用) | 多重継承が可能 |
| TypeScript | abstract class | interface | インターフェースのみ複数可 |
まとめ
本記事では、抽象クラスとインターフェースの違い・使い分け・各言語での実装方法について解説しました。
抽象クラスは「is-a関係」と共通実装の共有が必要な場合に、インターフェースは「can-do関係」と多重実装の柔軟性が必要な場合に使用するのが基本的な使い分けの指針です。
両者の特性を正しく理解し設計の意図に合わせて使い分けることが、保守性と拡張性の高いオブジェクト指向設計の実現につながります。
実際のコードで両者を意識的に使い分ける習慣をつけ、設計力を高めていきましょう。