信頼度ランク
| 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は「外部への契約」だ。定数も外部から使われることを前提にしている。privateやprotectedの定数を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だ。privateもprotectedも書けない(Java 8まで)。
なぜpublicのみか?interfaceは「外部への契約」なので、実装を隠すことが目的ではない。
なぜabstractか?元々interfaceはデフォルト実装を持てなかったため、すべてのメソッドが実装なし=abstractだった。
Java 8以降はdefaultとstaticメソッドが追加され、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つの本質から、制約の大半が論理的に導ける。