Java | オブジェクト指向:抽象メソッド

Java Java
スポンサーリンク

抽象メソッドとは

抽象メソッドは「中身(実装)がないメソッド」で、宣言だけを持ちます。abstract を付けて宣言し、具体的な処理は子クラスが必ず実装します。抽象クラスに定義して、共通の“名前・引数・戻り値”という契約を先に決め、やり方の違いは子クラスへ委ねるための仕組みです。これにより、呼び出し側は共通の型で扱いながら、実体に応じて振る舞いが切り替わる多態性が成立します。


使う理由と効果

抽象メソッドを使う最大の理由は「共通の契約を定めて、差し替え可能な拡張点を作る」ことです。親クラスは処理の骨格や前処理・検証を持ち、差し替えたいステップだけ抽象メソッドにします。子クラスが実装を提供することで、呼び出し側に分岐や詳細知識を持たせず、拡張や変更の影響を最小化できます。テストでも、抽象メソッドを実装したテスト用の子クラスに差し替えるだけで、振る舞いをコントロールできます。


基本ルールと宣言の仕方

宣言と実装の必須関係

抽象メソッドは「本体なし」で宣言します。親クラスが abstract である必要があり、子クラスはそのメソッドを実装しないとコンパイルエラーになります。実装したくない子クラスは、子自身を abstract にすればさらに下位での実装に委ねられます。

abstract class Importer {
    public final void run(String path) {
        String raw = load(path);
        String normalized = normalize(raw);
        save(normalized);
    }
    protected abstract String load(String path);   // 抽象メソッド(本体なし)
    protected String normalize(String s) {         // 親の共有ロジック
        return s == null ? "" : s.trim();
    }
    protected abstract void save(String data);     // 抽象メソッド
}

final class CsvImporter extends Importer {
    @Override protected String load(String path) { return "a,b,c"; }
    @Override protected void save(String data) { System.out.println("saved: " + data); }
}
Java

使えない修飾子と注意点

抽象メソッドは final・static・private にはできません。final は「上書き禁止」と矛盾し、static はクラスメソッドのため動的切替の対象外、private は子クラスから見えないため契約の強制ができないためです。アクセス修飾子は public/protected/package-private のいずれかを選び、契約の公開範囲を明確にします。


テンプレートメソッドとの連携(重要)

抽象メソッドは、テンプレートメソッドと組み合わせると真価を発揮します。親が処理の流れを final で固定し、差し替えたいステップだけ抽象化することで、安全に拡張できます。前処理・検証・監査など「必ず通してほしい」共通処理は親が持ち、個別の手順は抽象メソッドとして子に任せる形が理想です。

abstract class SecureService {
    public final void execute(String user) {       // 流れを固定
        audit("start", user);
        doExecute(user);                           // 差し替えポイント
        audit("end", user);
    }
    protected abstract void doExecute(String user);
    private void audit(String phase, String user) { System.out.println("[AUDIT] " + phase + " by " + user); }
}

final class TransferService extends SecureService {
    @Override protected void doExecute(String user) { System.out.println("transfer by " + user); }
}
Java

この形だと、監査を抜き忘れる事故を親で防げます。拡張は抽象メソッドの実装だけに集中できます。


例題で身につける

例 1: ストレージの差し替え

親が「保存」の契約を定め、保存先の違い(メモリ・ファイルなど)を子で実装します。

abstract class Storage {
    public final void put(String key, String value) {
        if (key == null || key.isBlank()) throw new IllegalArgumentException("key");
        if (value == null) throw new IllegalArgumentException("value");
        doPut(key.trim(), value);
    }
    protected abstract void doPut(String key, String value);
}

final class MemoryStorage extends Storage {
    private final java.util.Map<String, String> map = new java.util.HashMap<>();
    @Override protected void doPut(String key, String value) { map.put(key, value); }
}

final class FileStorage extends Storage {
    @Override protected void doPut(String key, String value) { System.out.println("write: " + key + "=" + value); }
}
Java

呼び出し側は Storage 型だけを知っていればよく、実体に応じて保存方法が切り替わります。

例 2: フォーマッタの段階化

親が基本の整形を持ち、仕上げの規則だけ子で差し替えます。

abstract class Formatter {
    public final String format(String raw) {
        String s = preprocess(raw);
        return postprocess(s);
    }
    protected String preprocess(String s) { return s == null ? "" : s.trim().replaceAll("\\s+", " "); }
    protected abstract String postprocess(String s);
}

final class UpperFormatter extends Formatter {
    @Override protected String postprocess(String s) { return s.toUpperCase(); }
}
Java

つまずきポイントと回避(重要)

コンストラクタで“仮想呼び出し”をしない

抽象メソッドはオーバーライドされる前提のメソッドです。親のコンストラクタ内で、オーバーライド可能なメソッド(抽象または非 final)を呼ぶと、子のフィールドが未初期化のまま参照される危険があります。初期化は「親の完成→子の完成」の順序を崩さない設計にし、コンストラクタ内で仮想呼び出しを避けてください。

契約の曖昧さを残さない

抽象メソッドの引数の前提、戻り値の意味、例外方針を親で明文化し、共通の検証は親で行います。子が自由に実装しても整合性が保たれるよう、差し替えポイントは小さく限定します。

インターフェースとの使い分け

「状態(フィールド)や共通実装を親で持ちたい」なら抽象クラス。「軽い契約だけを複数組み合わせたい」ならインターフェース。インターフェースのメソッドも抽象ですが、default を使えば簡易実装を提供できます。両者を併用し、抽象クラスは1系統に絞るのが安定します。


仕上げのアドバイス(重要部分のまとめ)

抽象メソッドは「型としての約束を決め、差し替えるべき処理だけを子に任せる」ための要です。親は流れ・前処理・検証を持ち、抽象メソッドで拡張点を明確に切る。コンストラクタで仮想呼び出しを避け、契約の意味を明文化し、差し替え範囲は最小に保つ——この型を守れば、拡張しやすく壊れにくい設計が手に入ります。

タイトルとURLをコピーしました