6.41:型を定義するためにマーカーインターフェースを使う
要点
「型」を表したければマーカー インターフェース を使う。
単にメタデータを付けたいだけならアノテーションを使う。
マーカーインターフェースは「その型であること」をコンパイル時に表現・チェックできる唯一の手段。
なぜマーカーインターフェースを使うのか
- マーカーインターフェースは 型そのもの を定義する(
instanceofや型境界<T extends Marker>、メソッド引数型に使える)。 - API の契約として「この種のオブジェクトだけ受け入れる」ことを明確にできる。
- 実行時に
obj instanceof Markerやif (obj instanceof Serializable)のように簡単に判定できる(アノテーションはこれができない)。 - 設計上「振る舞いではなく分類(is-a)」を表すのに自然。
具体例
// マーカーインターフェースを定義
public interface Storable { }
// 型として使える
public class User implements Storable {
private final String name;
// ...
}
// API が型を受ける(マーカーを使って契約を表す)
public void save(Storable s) {
// s は Storable(保存可能)であることが保証される
}
// ジェネリクスの境界にも使える
public <T extends Storable> void bulkSave(Collection<T> items) { ... }
// 実行時判定
if (obj instanceof Storable) {
// 安全に保存処理に回せる
}
★ この「型として扱える」性質がマーカーインターフェースの最大の利点。
マーカーインターフェース vs マーカーアノテーション(いつどちらを使うか)
マーカーインターフェースが向く場面
- 「そのオブジェクトが型的に扱える」ことを表現したいとき(APIの型境界にしたい)。
- たとえば「このオブジェクトは永続化可能」「このオブジェクトは比較可能な性質を持つ」とコンパイル時に表現したい場合。
マーカーアノテーションが向く場面
- 単にメタデータ(例:テストメソッドにつける
@Test、nullness 注釈など)を付けたいとき。 - 既存クラスに後付けで属性を与えたい場合(アノテーションは既存クラスに破壊的変更なく付けられる)。
- アノテーションはツールやフレームワークが読み取りやすく、属性(要素)を持たせられる。
重要な違い:
アノテーションは型ではないので instanceof やジェネリックの境界に使えない。
ベストプラクティス
-
API の一部にするか慎重に決める:
マーカーインターフェースを公開する = 利用者が実装する型を決めさせるということ。後から撤回・変更しにくい。 -
将来の拡張:
Java 8 以降はインターフェースにdefaultメソッドを追加できる(後方互換を保てる)が、既存実装への影響は考える(default を与えられないと破壊的)。設計時に拡張点を考慮。 -
ドキュメントを付ける:
何を意味する型なのか、実装者に期待すること(副作用/スレッド安全性など)を Javadoc で明確に。 -
アノテーションの併用も検討:
型としてのチェックはマーカーインターフェースで、追加のメタデータはアノテーションで与える、という組み合わせはよく使われる。 -
不適切な用途を避ける:
単に「このクラスに印を付けたいだけ(メタ情報)」ならアノテーションを選ぶ。マーカーインターフェースは型を定義するために使うべき。
まとめ
- 「APIで型として扱いたい/コンパイル時に安全にしたい」 ➡ マーカーインターフェース。
- 「ただメタ情報を付けたい・後付けしたい」 ➡ マーカーアノテーション。
- 両方の利点が欲しいときは マーカーインターフェース + 補助アノテーション の組み合わせを検討。