41.型定義のためにマーカーインターフェースを使うべし
マーカーインターフェースとは、メソッド定義を含まず、単にそのインターフェースを実装したクラスが特定の属性を持っていることを示すためのものである。
例として、Serializable
インターフェースがあげられる(12章)。このインターフェースを実装することで、そのクラスのインスタンスはObjectOutputStream
に書き込み可能であることが示される。
マーカーアノテーション(Item39)の登場で、マーカーインターフェースは時代遅れになったように思えるかもしれないが、それは誤りである。
マーカーインターフェースはマーカーアノテーションに比べて、2つの点でアドバンテージがある。
1つ目はマーカーインターフェースは型を定義するが、マーカーアノテーションはしないことである。これによって、マーカーアノテーションでは実行時まで捕捉できないエラーがコンパイル時に捕捉できることとなる。
Javaのシリアライゼーション機能では、型がシリアライザブルであることを示すために、Serializable
マーカーインターフェースを使用している。ObjectOutputStream.writeObject
メソッドでは、渡されたオブジェクトをシリアライズするが、その引数はシリアライザブルであることを求める。このメソッドの引数がSerializable
であれば(実際にはObjectをとる)、不適切な型の引数が来た場合にコンパイルで検知できる。
2つ目はマーカーインターフェースの方がより簡潔に対象を絞れることにある。
マーカーアノテーションのターゲットがElementType.TYPE
である場合は全てのクラスとインターフェースが対象となるが、マーカーインターフェースの場合はそれを実装しているクラスに限られる。
Set
インターフェースは、そのような制限するマーカーインターフェースである。
SetはCollectionのサブタイプにのみ実装されるが、Collectionで定義されたメソッドに何も付け加えていない。SetはCollectionのメソッドの一部を再定義しているので、一般にマーカーインターフェースとはみなされていない。
しかし、特定のインターフェースのサブタイプにのみ適用され、インターフェースのメソッドを再定義しないマーカーインターフェースは想像しやすい。そのようなマーカーインターフェースは、オブジェクト全体のある不変値(?)を記述したり、そのインスタンスは他の特定のクラスのメソッドによって処理されるように適合していることを指し示す(この意味では、Serializable
インターフェースは、そのインスタンスがObjectOutputStream
に処理されることに適合していることを指し示している)。
**マーカーアノテーションの利点は、大きなアノテーション機能の一部であるという点にある。**それゆえ、マーカーアノテーションはアノテーションベースのフレームワークで一貫性が考慮されている。
マーカーアノテーション、マーカーインターフェースをどう使い分けるか?
まず、クラスとインターフェース以外の要素にマーカーをつけるには、マーカーアノテーションを使用するしかない。
クラス、インターフェースへのマーカーに関しては、
このマーキングをしたオブジェクトのみを対象としたメソッドを1つ以上書くことがあるか?
を考えてみる。
もし書くことがあるなら、マーカーインターフェースを利用するべき。これによって、メソッドの引数の型チェックをコンパイル時に行うことができる。
もし書かないのであれば、マーカーアノテーションを利用すべき。
アノテーションをたくさん使うフレームワークにおけるマーキングであれば、マーカーアノテーションを選ぶべき。