抽象クラスとインターフェイスの違いと使い分け【完全ガイド2025】

 

はじめに

オブジェクト指向プログラミングにおいて、抽象クラスインターフェイスの使い分けは多くの開発者が迷うポイントです。この記事では、両者の違いから実践的な使い分けまで、具体例とともに詳しく解説します。

抽象クラスとは

抽象クラスは、共通の実装を持ちながら、一部のメソッドを抽象メソッドとして定義するクラスです。インスタンス化はできませんが、継承によって具体的な実装を強制できます。

抽象クラスの特徴

  • インスタンス化不可
  • 具体的なメソッドと抽象メソッドの両方を持てる
  • フィールド(プロパティ)を持てる
  • コンストラクタを持てる
  • 単一継承のみ

抽象クラスの例(Java)

abstract class Animal {
    protected String name;
    
    public Animal(String name) {
        this.name = name;
    }
    
    public void sleep() { // 具体的な実装
        System.out.println(name + " is sleeping");
    }
    
    public abstract void makeSound(); // 抽象メソッド
}

class Dog extends Animal {
    public Dog(String name) {
        super(name);
    }
    
    public void makeSound() {
        System.out.println(name + " says Woof!");
    }
}

インターフェイスとは

インターフェイスは、クラスが実装すべきメソッドの契約を定義する仕組みです。メソッドのシグネチャのみを定義し、実装は継承先のクラスに委ねます。

インターフェイスの特徴

  • インスタンス化不可
  • 抽象メソッドのみ定義(Java 8以降はデフォルトメソッドも可能)
  • 定数のみ持てる(final static変数)
  • 多重実装可能
  • Java 8以降はdefaultメソッドとstaticメソッドも定義可能

インターフェイスの例(Java)

interface Flyable {
    void fly();
}

interface Swimmable {
    void swim();
}

class Duck implements Flyable, Swimmable {
    public void fly() {
        System.out.println("Duck is flying");
    }
    
    public void swim() {
        System.out.println("Duck is swimming");
    }
}

抽象クラスとインターフェイスの比較表

項目 抽象クラス インターフェイス
インスタンス化 不可 不可
実装メソッド 可能 Java 8以降は部分的に可能
フィールド 可能 定数のみ
コンストラクタ 可能 不可
継承 単一継承 多重実装
アクセス修飾子 自由 public(基本)

使い分けの基準

抽象クラスを使うべき場面

1. 共通実装がある場合

abstract class Shape {
    protected String color;
    
    public void setColor(String color) { // 共通実装
        this.color = color;
    }
    
    public abstract double getArea(); // 各図形で異なる実装
}

class Circle extends Shape {
    private double radius;
    
    public Circle(double radius) {
        this.radius = radius;
    }
    
    public double getArea() {
        return Math.PI * radius * radius;
    }
}

2. 段階的な機能拡張が必要な場合

abstract class Vehicle {
    public void start() {
        System.out.println("Engine started");
    }
    
    public abstract void accelerate();
}

abstract class Car extends Vehicle {
    public void openDoor() {
        System.out.println("Door opened");
    }
}

class SportsCar extends Car {
    public void accelerate() {
        System.out.println("Accelerating rapidly!");
    }
}

インターフェイスを使うべき場面

1. 契約の定義

interface PaymentProcessor {
    boolean processPayment(double amount);
    void sendReceipt(String email);
}

class CreditCardProcessor implements PaymentProcessor {
    public boolean processPayment(double amount) {
        // クレジットカード決済処理
        return true;
    }
    
    public void sendReceipt(String email) {
        // 領収書送信
    }
}

2. 多重実装が必要な場合

interface Readable {
    void read();
}

interface Writable {
    void write();
}

class File implements Readable, Writable {
    public void read() {
        System.out.println("Reading file");
    }
    
    public void write() {
        System.out.println("Writing file");
    }
}

実践的な使い分けパターン

パターン1: テンプレートメソッドパターン

abstract class DataProcessor {
    public final void process() { // テンプレートメソッド
        loadData();
        processData();
        saveData();
    }
    
    protected abstract void loadData();
    protected abstract void processData();
    
    protected void saveData() { // 共通実装
        System.out.println("Data saved");
    }
}

パターン2: 戦略パターン

interface SortStrategy {
    void sort(int[] array);
}

class BubbleSort implements SortStrategy {
    public void sort(int[] array) {
        // バブルソート実装
    }
}

class QuickSort implements SortStrategy {
    public void sort(int[] array) {
        // クイックソート実装
    }
}

言語別の特徴

Java 8以降の変更点

interface Calculator {
    int add(int a, int b);
    
    default int multiply(int a, int b) { // デフォルト実装
        return a * b;
    }
    
    static int subtract(int a, int b) { // 静的メソッド
        return a - b;
    }
}

C#の場合

// 抽象クラス
abstract class BaseService {
    protected string connectionString;
    
    public abstract void Execute();
    
    public void Log(string message) {
        Console.WriteLine($"Log: {message}");
    }
}

// インターフェイス
interface IRepository<T> {
    void Save(T entity);
    T FindById(int id);
}

よくある質問(FAQ)

Q: 抽象クラスとインターフェイスを両方使うべき?

A: はい。多くの場合、両方を組み合わせて使用します:

interface Drawable {
    void draw();
}

abstract class UIComponent implements Drawable {
    protected int x, y;
    
    public void setPosition(int x, int y) {
        this.x = x;
        this.y = y;
    }
}

Q: どちらを選ぶか迷った場合は?

A: 以下の優先順位で判断してください:

  1. 多重継承が必要 → インターフェイス
  2. 共通実装がある → 抽象クラス
  3. 契約のみ定義したい → インターフェイス
  4. 迷った場合 → インターフェイス(より柔軟)

Q: パフォーマンスに差はありますか?

A: 実行時のパフォーマンスはほぼ同じです。設計の柔軟性とメンテナンス性を重視して選択してください。

ベストプラクティス

1. インターフェイス分離の原則

// 悪い例:肥大化したインターフェイス
interface Worker {
    void work();
    void eat();
    void sleep();
}

// 良い例:分離されたインターフェイス
interface Workable {
    void work();
}

interface Eatable {
    void eat();
}

2. 抽象クラスでの共通実装の活用

abstract class BaseEntity {
    protected Long id;
    protected Date createdAt;
    
    public BaseEntity() {
        this.createdAt = new Date();
    }
    
    public abstract void validate();
}

3. インターフェイスでの依存性注入

class OrderService {
    private final PaymentProcessor paymentProcessor;
    
    public OrderService(PaymentProcessor paymentProcessor) {
        this.paymentProcessor = paymentProcessor;
    }
}

まとめ

抽象クラスとインターフェイスの使い分けは、以下のポイントを押さえることが重要です:

抽象クラスを選ぶべき場合:

  • 共通の実装コードがある
  • 段階的な継承が必要
  • is-a関係を表現したい

インターフェイスを選ぶべき場合:

  • 契約や能力を定義したい
  • 多重実装が必要
  • can-do関係を表現したい
  • より柔軟な設計にしたい

適切な使い分けにより、保守性が高く拡張しやすいコードを書くことができます。実際の開発では、両方を組み合わせて使用することが多いため、それぞれの特徴を理解して最適な選択をしてください。


関連記事

■プロンプトだけでオリジナルアプリを開発・公開してみた!!

■AI時代の第一歩!「AI駆動開発コース」はじめました!

テックジム東京本校で先行開始。

■テックジム東京本校

「武田塾」のプログラミング版といえば「テックジム」。
講義動画なし、教科書なし。「進捗管理とコーチング」で効率学習。
より早く、より安く、しかも対面型のプログラミングスクールです。

<短期講習>5日で5万円の「Pythonミニキャンプ」開催中。

<月1開催>放送作家による映像ディレクター養成講座

<オンライン無料>ゼロから始めるPython爆速講座