28.

Java 抽象クラス・抽象メソッドの構文と具体例完全ガイド

編集
この記事の要点
  • 抽象クラス (abstract class)インスタンス化できないクラス。サブクラスでの実装を前提とする
  • 抽象メソッド (abstract method) は本体を持たない (abstract void foo();)。継承先で @Override 必須
  • 抽象クラスはコンストラクタを持てる、フィールドも持てる、具象メソッドと混在可
  • staticfinalprivate なメソッドは abstract と併用不可
  • interface との違い: 抽象クラスは「is-a」+ 状態、interface は「能力」(can-do) + 多重実装可
  • Java 17+ の sealed class で継承先を限定可能 (sealed permits Dog, Cat)
  • 典型用途: Template Method / Strategy / 共通実装+一部だけ拡張

抽象クラス・抽象メソッドの基本構文

Java の abstract キーワードでクラスやメソッドを「抽象」として宣言できます。抽象クラスは自分自身では new できず、サブクラスで具体化されることを前提とした設計用クラスです。

// 抽象クラスの宣言
abstract class Animal {
    // 共通フィールド
    protected String name;

    // コンストラクタ (抽象クラスでも可)
    public Animal(String name) {
        this.name = name;
    }

    // 具象メソッド (普通のメソッド)
    public String getName() {
        return name;
    }

    // 抽象メソッド (本体なし)
    public abstract void sound();
}

// サブクラスで実装
class Dog extends Animal {
    public Dog(String name) {
        super(name);
    }

    @Override
    public void sound() {
        System.out.println(name + ": ワン");
    }
}

class Cat extends Animal {
    public Cat(String name) { super(name); }
    @Override
    public void sound() {
        System.out.println(name + ": ニャー");
    }
}

public class Main {
    public static void main(String[] args) {
        // Animal a = new Animal("x");  // ❌ コンパイルエラー
        Animal a = new Dog("ポチ");
        a.sound();   // ポチ: ワン
    }
}

抽象クラスのルール

項目可否説明
インスタンス化 (new)不可サブクラス経由のみ
コンストラクタサブクラスから super(...) で呼ばれる
フィールドprotected / private OK
具象メソッド共通処理を集約できる
抽象メソッド1 つもなくても良いがその場合は抽象にする意味薄
static abstract不可static は継承で override 不可
final abstract不可final は継承禁止と矛盾
private abstract不可private はサブクラスから見えず override 不能

Template Method パターン

抽象クラスの最も典型的な使い方。「処理の骨格を親で固定、一部だけ子で変える」設計。

abstract class ReportGenerator {
    // テンプレートメソッド (final で上書き禁止)
    public final void generate() {
        loadData();
        String body = formatBody();    // 子で実装
        String html = renderHtml(body);
        save(html);
    }

    private void loadData() { System.out.println("DB から読込"); }
    protected abstract String formatBody();   // ★ 子で変える
    private String renderHtml(String body) {
        return "<html>" + body + "</html>";
    }
    private void save(String html) { System.out.println("保存: " + html); }
}

class SalesReport extends ReportGenerator {
    @Override
    protected String formatBody() {
        return "<h1>売上レポート</h1><p>...</p>";
    }
}

class UserReport extends ReportGenerator {
    @Override
    protected String formatBody() {
        return "<h1>ユーザレポート</h1><p>...</p>";
    }
}

// 利用側
new SalesReport().generate();
new UserReport().generate();

Strategy パターン (抽象クラス版)

Strategy は interface で書く方が一般的ですが、共通フィールド/コンストラクタが欲しい場合は抽象クラスで:

abstract class Discount {
    protected final String name;
    protected Discount(String name) { this.name = name; }
    public abstract int apply(int price);
}

class PercentDiscount extends Discount {
    private final int percent;
    public PercentDiscount(int p) {
        super("PCT" + p);
        this.percent = p;
    }
    @Override
    public int apply(int price) {
        return price - price * percent / 100;
    }
}

class FixedDiscount extends Discount {
    private final int amount;
    public FixedDiscount(int a) {
        super("FIX" + a);
        this.amount = a;
    }
    @Override
    public int apply(int price) {
        return Math.max(0, price - amount);
    }
}

// 利用
Discount d = new PercentDiscount(10);
System.out.println(d.apply(1000));   // 900

抽象クラス vs インタフェース

項目abstract classinterface
多重継承不可 (1 親のみ)可 (複数 implements)
状態 (フィールド)持てる定数 (public static final) のみ
コンストラクタ持てる不可
default メソッド全メソッド OKJava 8+ で可
アクセス修飾子自由メソッドは原則 public
意味的役割「is-a」(共通実装含む型階層)「can-do」(能力)
使い分け共通実装が大量にあるとき純粋な契約だけ表現したいとき

判断基準: 共通フィールドや具象メソッドが多いなら抽象クラス、能力の定義だけなら interface。両方を組み合わせる (interface を実装した抽象クラスを作る) のも一般的。

Java 17 sealed class との組合せ

Java 17+ では sealed継承可能な子クラスを限定できます。抽象クラスと組合せると網羅的な switch (パターンマッチ) が書けます:

// 図形を Circle / Square / Triangle に限定
sealed abstract class Shape permits Circle, Square, Triangle {
    public abstract double area();
}

final class Circle extends Shape {
    private final double r;
    Circle(double r) { this.r = r; }
    @Override public double area() { return Math.PI * r * r; }
}

final class Square extends Shape {
    private final double s;
    Square(double s) { this.s = s; }
    @Override public double area() { return s * s; }
}

final class Triangle extends Shape {
    private final double b, h;
    Triangle(double b, double h) { this.b = b; this.h = h; }
    @Override public double area() { return b * h / 2; }
}

// Pattern matching for switch (Java 21+)
String desc = switch (shape) {
    case Circle c   -> "円 r=" + c;
    case Square s   -> "正方形";
    case Triangle t -> "三角形";
    // default 不要 (sealed で網羅性チェック)
};

典型的なミスと回避

ミス結果対処
abstract メソッドに本体を書くコンパイルエラー; で終わらせる
abstract クラスを new するコンパイルエラーサブクラス経由で new
サブクラスで abstract メソッド未実装サブクラスも abstract 扱いになる全 abstract を override or サブクラスも abstract に
@Override 忘れシグネチャ違いで別メソッドになり気付かない必ず付ける (IDE 警告 ON)
深い継承 (5 階層以上)Fragile Base Class 問題Composition Over Inheritance を検討

FAQ

Q: 抽象クラスに具象メソッドだけで abstract メソッドが 1 つもないと?
A: 文法的には可能。new 禁止だけが残ります。が、設計として中途半端で意味薄。通常は protected コンストラクタの普通クラスにします。

Q: abstract クラスは interface で全部書けない?
A: Java 8+ の default メソッドでかなり代替可能。ただし状態を持つフィールドコンストラクタは interface では不可。状態が必要なら抽象クラス。

Q: 抽象クラスを継承した上でさらに抽象にできる?
A: 可能。抽象 → 抽象 → 具象 の 3 段継承も OK。ただし深い継承は保守性を下げます。

編集
Post Share
子ページ

子ページはありません

同階層のページ
  1. 基本事項
  2. HTMLへの埋め込み
  3. 変数
  4. 可変変数
  5. 定数
  6. データ型
  7. キャスト
  8. エスケープ文字
  9. 配列
  10. 演算子
  11. 代入の際の注意点
  12. 条件分岐
  13. 繰り返し処理
  14. クラスとインスタンス
  15. コンストラクタ
  16. 関数
  17. スーパーグローバル変数
  18. スコープ
  19. staticについて
  20. yieldについて
  21. ファイルのアップロード方法
  22. DB接続方法
  23. SQL実行方法
  24. カプセル化の具体例
  25. 継承の構文
  26. オーバーライド
  27. ポリモーフィズム(多様性)の具体例
  28. 抽象クラス・メソッドの構文と具体例
  29. GET通信
  30. try catchで全てのエラーを拾う方法

最近更新/作成されたページ