SJ blog
beginner
S

信頼度ランク

S 公式ソース確認済み
A 成功実績多数・失敗例少数
B 賛否両論
C 動作未確認・セキュリティリスク高
Z 個人所感

abstractとinterfaceの禁止事項 — private・static・finalとの組み合わせ制約

abstractメソッドがprivate・static・finalと組み合わせられない理由、interfaceのフィールドがpublic static finalである理由、abstractクラスにコンストラクタがある理由を解説。

一言結論

abstractメソッドの禁止事項(private/static/final)は「オーバーライドされなければ意味がない」という本質から導ける。interfaceのフィールドがpublic static finalである理由もインスタンスを持たない設計から必然。

abstractメソッドとは「委任の宣言」だ

まずabstractの本質を確認する。

abstract class Shape {
    abstract double area();  // 「面積を計算するメソッドをもつこと」の宣言
}

abstractメソッドは「このメソッドの実装は子クラスに任せる」という宣言だ。オーバーライドされることを前提にしている

ここから、3つの禁止事項が導ける。

abstractとprivateを組み合わせられない

abstract class Shape {
    private abstract double area();  // ❌ コンパイルエラー
}

privateは「このクラスの中からしかアクセスできない」という制約だ。つまり子クラスからも見えない。

子クラスから見えないメソッドはオーバーライドできない。

abstractは「子クラスよオーバーライドせよ」と要求する。privateは「子クラスよ見るな」と制限する。この2つは真っ向から矛盾する。コンパイラは矛盾を許さない。

protectedならOKだ(子クラスからは見える):

abstract class Shape {
    protected abstract double area();  // ✅ 子クラスからアクセス可能でオーバーライドできる
}

abstractとstaticを組み合わせられない

abstract class Shape {
    static abstract double defaultArea();  // ❌ コンパイルエラー
}

staticメソッドはクラスに属し、インスタンスに属さない。そしてstaticメソッドはオーバーライドできない(オーバーライドの記事で説明した通り、staticはハイディングになる)。

abstractは「オーバーライドせよ」という要求。staticは「オーバーライドできない」という性質。矛盾だ。

abstractとfinalを組み合わせられない

abstract class Shape {
    final abstract double area();  // ❌ コンパイルエラー
}

finalメソッドは「オーバーライド禁止」の宣言だ。

abstractは「オーバーライドせよ」。finalは「オーバーライドするな」。正反対。

ついでに:abstractクラス自体をfinalにもできない

final abstract class Shape { }  // ❌ コンパイルエラー

abstract classは継承して使うものだ。final classは継承禁止。これも矛盾する。

abstractクラスにはコンストラクタがある

abstract class Shape {
    double x, y;  // フィールド

    Shape(double x, double y) {  // ✅ コンストラクタを持てる
        this.x = x;
        this.y = y;
    }
}

class Circle extends Shape {
    double radius;

    Circle(double x, double y, double radius) {
        super(x, y);  // 親のコンストラクタを呼ぶ
        this.radius = radius;
    }
}

「abstractクラスはnewできないのにコンストラクタがあるのはなぜ?」という疑問は正しい感覚だ。

コンストラクタは「子クラスがsuper()で呼ぶため」に存在する。子クラスがnew Circle()されたとき、Shapeのコンストラクタがsuper()経由で呼ばれ、Shapeの部分(xとy)が初期化される。

abstractクラスを直接newすることはできないが、コンストラクタ自体は必要だ。

interfaceのフィールドはpublic static finalだけ

interfaceにフィールドを書くと、自動的にpublic static finalになる:

interface Config {
    int MAX_SIZE = 100;
    // ↑ 実際は public static final int MAX_SIZE = 100;
}

なぜpublic static finalが強制されるのか。理由を一つずつ分解する。

なぜstaticか?

interfaceはインスタンスを持てない(コンストラクタがない、newできない)。インスタンスがないなら、インスタンスごとの値(インスタンスフィールド)も持てない。存在できるのはクラス(static)レベルの値だけだ。

なぜfinalか?

interfaceを複数のクラスがimplementsするとき、複数のクラスが同じフィールドを共有する。もし変更可能なら、あるクラスが変更した値が全体に影響する。interfaceのフィールドは「定数の共有」のために使うものなので、変更不可にするのが安全だ。

なぜpublicか?

interfaceは「外部への契約」だ。定数も外部から使われることを前提にしている。privateprotectedの定数をinterfaceに置いても意味がない。

だからinterfaceのフィールドはすべて定数(public static final)のみ許可される。

interfaceにコンストラクタはない

interface Flyable {
    Flyable() { }  // ❌ コンパイルエラー
}

interfaceはnewできない(インスタンスを生成しない)。コンストラクタはインスタンスを初期化するためのものだから、インスタンスを作らないinterfaceにコンストラクタは不要だ。

abstractクラスとの違いはここだ:abstractクラスは継承先がsuper()で呼ぶためのコンストラクタを持てる。interfaceにはsuper()で呼ばれるコンストラクタが存在しない(interfaceのimplementsはsuper()を呼ばない)。

interfaceのメソッドはデフォルトでpublic abstract

interface Animal {
    void breathe();
    // ↑ 実際は public abstract void breathe();
}

interfaceのメソッドは暗黙的にpublic abstractだ。privateprotectedも書けない(Java 8まで)。

なぜpublicのみか?interfaceは「外部への契約」なので、実装を隠すことが目的ではない。

なぜabstractか?元々interfaceはデフォルト実装を持てなかったため、すべてのメソッドが実装なし=abstractだった。

Java 8以降はdefaultstaticメソッドが追加され、Java 9以降はprivateメソッドも追加された(defaultメソッドの内部実装を共通化するため):

interface Logger {
    void log(String message);

    default void logError(String message) {
        log("[ERROR] " + message);  // defaultメソッド
    }

    static Logger noOp() {
        return message -> { };  // staticファクトリメソッド(Java 8)
    }

    private void validate(String message) {
        // privateメソッド(Java 9以降、defaultメソッドの補助処理用)
    }
}

まとめ

abstractとprivateの組み合わせ禁止
  → privateはオーバーライドできない。abstractはオーバーライドを要求。矛盾

abstractとstaticの組み合わせ禁止
  → staticはオーバーライドできない(ハイディングになる)。abstractと矛盾

abstractとfinalの組み合わせ禁止
  → finalはオーバーライド禁止。abstractはオーバーライド要求。矛盾

abstractクラスのコンストラクタ
  → newできないが、子クラスがsuper()で呼ぶために存在する

interfaceのフィールドはpublic static final
  → インスタンスがない(static)、変更を防ぐ(final)、外部への公開(public)が自然な帰結

interfaceにコンストラクタなし
  → インスタンスを生成しないため不要

abstractやinterfaceの制約を個別に暗記するのは辛い。「abstractはオーバーライドが前提」「interfaceはインスタンスを持たない」という2つの本質から、制約の大半が論理的に導ける。