はじめに

Java学習において、最初に混乱しやすいテーマの一つが「interfaceabstract classの違いと使い分け」です。
構文的な違いは理解できても、「どちらを使うべきか」「なぜこの構造で設計されたのか」という点が曖昧なまま進んでしまうケースが少なくありません。

この記事では、interfaceabstract classを単なる文法ではなく設計の抽象軸として捉え、
「猫と犬」を例に能力と構造の分離という観点からその違いを明確にしていきます。

正しい設計から入る:能力と構造を分離するクラス設計

まずは、整理された構造設計を提示します。
この例では、interfaceは「鳴く能力(= 行動)」、abstract classは「哺乳類としての構造的共通性(= 状態と機能)」を表現しています。

鳴くという能力に焦点を当てたinterface
// 生物が鳴く能力を抽象化したインターフェース
public interface SoundEmitter {
    void makeSound();
}
哺乳類の共通構造を抽象化したクラス
// 名前と呼吸機能を共有する抽象クラス
public abstract class Mammal implements SoundEmitter {
    protected String name; // 名前はすべての哺乳類が持つ共通状態
    public Mammal(String name) {
        this.name = name;
    }
    public void breathe() {
        System.out.println(name + " is breathing."); // 共通の機能
    }
}
DogとCatの具象クラス実装
// Dogクラスは鳴き声の具体実装を持つ
public class Dog extends Mammal {
    public Dog(String name) {
        super(name);
    }
    @Override
    public void makeSound() {
        System.out.println("Woof!"); // 犬の鳴き声
    }
}
// Catクラスも同様に、鳴き声を実装
public class Cat extends Mammal {
    public Cat(String name) {
        super(name);
    }
    @Override
    public void makeSound() {
        System.out.println("Meow!"); // 猫の鳴き声
    }
}
UML Diagram

この設計のポイントは:

  • SoundEmitter は「鳴ける存在」という能力の抽象
  • Mammal は「名前を持ち呼吸する存在」という構造の共通化
  • DogCat はその具象化

interfaceを使うべき場面とは

能力に着目したFlyable interface
// 飛ぶという能力を抽象化したインターフェース
public interface Flyable {
    void fly();
}
鳥と飛行機がそれぞれの方法で飛ぶ
// 鳥が羽ばたいて飛ぶ
public class Bird implements Flyable {
    public void fly() {
        System.out.println("Flap flap");
    }
}
// 飛行機が推進力で飛ぶ
public class Airplane implements Flyable {
    public void fly() {
        System.out.println("Jet propulsion");
    }
}

このように、異なる系統のクラスでも共通の能力を持たせられるのがinterfaceの強みです。

abstract classを使うべき場面とは

WebアプリのController共通クラス例
// 全てのコントローラが共通の状態と振る舞いを持つ
public abstract class BaseController {
    protected String userId; // 全てのリクエストがユーザーIDを持つ前提
    public BaseController(String userId) {
        this.userId = userId;
    }
    public abstract void execute(); // 各コントローラで実装されるメソッド
}

抽象クラスは、「実装を強制する枠組み」をつくることで再利用と制限の両立を図れます。

よくある誤用:Animal ⇨ Mammal の曖昧な継承構造

責務が曖昧なinterfaceの例
// 本来は構造に属するメソッドまで含んでしまっている
public interface Animal {
    void makeSound();
    void breathe(); // 呼吸は共通機能。interfaceに置くのは不適切
}
意図が曖昧なabstract classの継承
public abstract class Mammal implements Animal {
    protected String name;
    public Mammal(String name) {
        this.name = name;
    }
    @Override
    public void breathe() {
        System.out.println("breathing..."); // 本来は構造として定義すべき
    }
}
Comment
@Reviewer: AnimalとMammalは分類の粒度が近すぎます。interfaceは能力(can)、abstract classは構造(is-a)で軸を分離しましょう。

チェックリスト:レビューで注目すべきポイント

interface/abstract classの設計レビュー観点

"Animal"と"Dog"のような分類ワードでの構成は、構造より分類に引っ張られることがあります。「能力」か「構造」かの視点が要となります。

まとめ

interfaceとabstract classの違いは、文法上のものではなく抽象軸と設計意図の違いです。

種類 対象 責務
interface 機能(can) 能力の契約(動作保証)
abstract class 構造(is-a) 共通実装の再利用と枠組み定義

動くコードを書くこと以上に、意図が読める構造を設計できるかがレビューでも問われます。
構造の整理がレビューしやすさと保守性につながるという視点を持つことが、初学者から中堅へステップアップする上での鍵です。