Javaのマーカーインターフェイス?


134

JavaのMarkerインターフェイスは空のインターフェイスであり、このインターフェイスを実装するクラスのオブジェクトをシリアル化、クローニングなどの特別な方法で処理する必要があることをコンパイラまたはJVMに通知するために使用されることを教えられていました

しかし、最近、実際にはコンパイラやJVMとは何の関係もないことを知りました。たとえば、Serializableインターフェースの場合、のメソッドwriteObject(Object)は、クラスが適切に実装およびスローするかどうかを検出するObjectOutputStreamようなものです。すべてがコードで処理され、これはデザインパターンのように見えるので、独自のマーカーインターフェイスを定義できると思います。instanceOf SerializableSerializableNotSerializableException

今私の疑問:

  1. 第一点で上記のマーカーインターフェースの定義は間違っていますか?では、マーカーインターフェイスをどのように定義できますか?

  2. そして、instanceOf演算子を使用する代わりに、なぜメソッドをwriteObject(Serializable)実行時ではなくコンパイル時の型チェックがあるようにすることができないのでしょうか?

  3. 注釈はマーカーインターフェイスよりも優れていますか?


8
Serializable注釈としては意味が@NonNullなく、インターフェースとしても意味がありません。私は言うでしょう:注釈はマーカー+メタデータです。ところで、Annotationsの前任者は、Javadocで生まれ、Annotationsによって殺されたXDocletでした。
グリム

回答:


117
  1. 第一点で上記のマーカーインターフェースの定義は間違っていますか?-(1)マーカーインターフェイスが空でなければならないこと、および(2)これを実装することは、実装クラスの特別な扱いを意味することを部分で正しく説明しています。正しくない部分は、JVMまたはコンパイラがそのクラスのオブジェクトを異なる方法で処理することを意味することです。これらのオブジェクトを複製可能、直列化可能などとして処理するのはJavaクラスライブラリのコードであることに注意してください。コンパイラやJVMとは関係ありません。
  2. instanceOf演算子を使用する代わりにwriteObject(Serializable)、コンパイル時の型チェックがあるようにメソッドを次のようにすることはできません。これにより、「プレーンObject」が必要なときに、マーカーインターフェイスの名前でコードが汚染されるのを回避できます。たとえば、シリアライズ可能である必要があり、オブジェクトメンバーを持つクラスを作成する場合Serializable、コンパイル時にキャストするか、オブジェクトを作成する必要があります。インターフェイスには機能がないため、これは不便です。
  3. 注釈はマーカーインターフェイスよりも優れていますか?-クラスのメタデータを別のタイプを作成せずにそのコンシューマーに伝達するという同じ目的を達成できます。アノテーションもより強力であり、プログラマーはより洗練された情報を、それを「消費」するクラスに渡すことができます。

14
私が常に理解している方法は、注釈は一種の「マーカーインターフェイス2.0」であるということです
。Java1.1

writeObjectプライベートなので、たとえば、クラスがスーパークラス実装を呼び出す必要がないことを意味します
ラチェットフリーク

15
留意すべき点の1つは、標準ライブラリが最初に設計されてから数年後に注釈が言語に追加されたことです。アノテーションが最初から言語であった場合、Serializableがインターフェースであったかどうかは疑わしく、おそらくアノテーションだったでしょう。
セオドアノーベル2014

まあ、Cloneableそれが変更されたインスタンスのライブラリまたはJVMの扱いかどうかは明らかではありません。
Holger

「[...]別のタイプを作成せずに。」-これが正確にそれらを分離するものだと私は言います:マーカーインターフェイス型を導入します、注釈(種類)導入しません
aioobe

22

非直列化可能クラスの子は直列化可能であるため、強制SerializableすることはできませんwriteObjectが、それらのインスタンスは親クラスにアップキャストされる場合があります。その結果、シリアル化Objectできないもの(など)への参照を保持しても、参照されるインスタンスを実際にシリアル化できないことを意味しません。たとえば

   Object x = "abc";
   if (x instanceof Serializable) {
   }

親クラス(Object)はシリアル化可能ではなく、パラメーターのないコンストラクターを使用して初期化されます。で参照される値はxString、直列化可能であるとの条件付きステートメントが実行されます。


6

Javaのマーカーインターフェイスは、フィールドやメソッドのないインターフェイスです。簡単に言うと、Javaの空のインターフェースはマーカーインターフェースと呼ばれます。マーカーインターフェイスの例はSerializableCloneableおよびRemoteインターフェイスです。これらは、コンパイラまたはJVMにいくつかの情報を示すために使用されます。したがって、JVMはクラスがSerializableであると認識した場合、そのクラスに対して特別な操作を実行できます。同様に、JVMは、いくつかのクラスが実装されCloneableていることを確認すると、複製をサポートするためにいくつかの操作を実行できます。RMIおよびRemoteインターフェースについても同様です。つまり、マーカーインターフェイスは、コンパイラまたはJVMへの信号またはコマンドを示します。

上記はブログ投稿のコピーとして始まりましたが、文法のために軽く編集されています。


6
コピーしても問題ありませんが、ソースも明記してください:javarevisited.blogspot.com/2012/01/…。また、スペルミスもコピーアンドペーストしないと便利です。:)
Saurabh Patil 2015年

6

私は疑いを解決するための簡単なデモンストレーションを行いました1と2:

MobilePhone.javaClass によって実装されるMovableインターフェースと、Movableインターフェースを実装LandlinePhone.javaないもう1つのクラスを使用します

マーカーインターフェイス:

package com;

public interface Movable {

}

LandLinePhone.java そして MobilePhone.java

 package com;

 class LandLinePhone {
    // more code here
 }
 class MobilePhone implements Movable {
    // more code here
 }

カスタム例外クラス:パッケージcom;

public class NotMovableException extends Exception {

private static final long serialVersionUID = 1L;

    @Override
    public String getMessage() {
        return "this object is not movable";
    }
    // more code here
    }

私たちのテストクラス: TestMArkerInterface.java

package com;

public class TestMarkerInterface {

public static void main(String[] args) throws NotMovableException {
    MobilePhone mobilePhone = new MobilePhone();
    LandLinePhone landLinePhone = new LandLinePhone();

    TestMarkerInterface.goTravel(mobilePhone);
    TestMarkerInterface.goTravel(landLinePhone);
}

public static void goTravel(Object o) throws NotMovableException {
    if (!(o instanceof Movable)) {
        System.out.println("you cannot use :" + o.getClass().getName() + "   while travelling");
        throw new NotMovableException();
    }

    System.out.println("you can use :" + o.getClass().getName() + "   while travelling");
}}

メインクラスを実行すると:

you can use :com.MobilePhone while travelling
you cannot use :com.LandLinePhone while travelling
Exception in thread "main" com.NotMovableException: this object is not movable
    at com.TestMarkerInterface.goTravel(TestMarkerInterface.java:22)
    at com.TestMarkerInterface.main(TestMarkerInterface.java:14)

したがって、どのクラスがマーカーインターフェイスを実装するか Movableをてもテストに合格します。そうでない場合、エラーメッセージが表示されます。

これは、SerializableCloneableなどのinstanceOf演算子チェックが行われる方法です


5

a /マーカーインターフェイスは、その名前が示すとおり、クラスが何かを宣言していることを知っているものに通知するためだけに存在します。何でも、SerializableインターフェースのJDKクラス、またはカスタムクラス用に独自に作成したクラスです。

b /マーカーインターフェイスの場合は、メソッドの存在を暗示するべきではありません。暗黙のメソッドをインターフェイスに含めることをお勧めします。しかし、なぜ それが必要なのかわかっていれば好きなように設計することができます。

c /空のインターフェースと、値またはパラメーターを使用しないアノテーションとの違いはほとんどありません。しかし、違いはそこにあります。アノテーションは、実行時にアクセスできるキー/値のリストを宣言できます。


3

a。私は常にそれらを設計パターンと見なしており、JVM-Specialは何も考えていません。そのパターンをいくつかの状況で使用しました。

c。注釈を使用して何かをマークする方が、マーカーインターフェイスを使用するよりも優れたソリューションであると思います。単にインターフェースがタイプ/クラスの一般的なインターフェースを定義することを目的としているからです。それらはクラス階層の一部です。

アノテーションはコードにメタ情報を提供することを目的としており、マーカーはメタ情報だと思います。したがって、それらはまさにそのユースケースに適しています。


3
  1. これは、JVMとコンパイラーとは(必ずしも)何の関係もありません。特定のマーカーインターフェイスに関心があり、そのテストを行っているすべてのコードに何らかの関係があります。

  2. これは設計上の決定であり、正当な理由で行われます。AudriusMeškauskasからの回答を参照してください。

  3. この特定のトピックに関しては、良くも悪くも問題ではないと思います。マーカーインターフェイスは、想定どおりの動作をしています。


「AudriusMeškauskasからの回答」へのリンクを追加できますか?このページにはその名前のものが何も表示されません。
サラメッサー2017年

2

マーカーインターフェイスの主な目的は、型自体が独自の動作を持たない特別な型を作成することです。

public interface MarkerEntity {

}

public boolean save(Object object) throws InvalidEntityFoundException {
   if(!(object instanceof MarkerEntity)) {
       throw new InvalidEntityFoundException("Invalid Entity Found, can't be  saved);
   } 
   return db.save(object);
}

ここでsaveメソッドは、MarkerEntityインターフェースを実装するクラスのオブジェクトのみが確実に保存されるようにします。他のタイプでは、InvalidEntityFoundExceptionがスローされます。したがって、ここでMarkerEntityマーカーインターフェイスは、それを実装するクラスに特別な動作を追加する型を定義しています。

アノテーションは現在、一部の特別な処理のためにクラスをマークするためにも使用できますが、マーカーアノテーションは、マーカーインターフェイスではなく、名前付けパターンの代わりになります。

しかし、マーカーアノテーションはマーカ​​ーインターフェイスを完全に置き換えることはできません。マーカーインターフェースは、タイプを定義するために使用されます(すでに上で説明したように)。

マーカーインターフェイスコメントのソース


1
プログラマが明示的にデータベースの例に保存できると明示的に言わなければならない単なる「安全」フックとして実際に使用されていることを示すための+1。
Ludvig W

1

SerializableとCloneableはマーカーインターフェイスの悪い例であることを最初に主張します。もちろん、これらはメソッドとのインターフェースですが、などのメソッドを意味しますwriteObject(ObjectOutputStream)。(コンパイラーは、writeObject(ObjectOutputStream)メソッドをオーバーライドしないと、すべてのオブジェクトに既にがあるclone()場合にclone()メソッドを作成しますが、コンパイラーは実際のメソッドを作成しますが、警告があります。これらは両方とも、実際にはそうではない奇妙なエッジケースです。良いデザイン例です。)

マーカーインターフェイスは通常、次の2つの目的のいずれかに使用されます。

1)多くのジェネリックで発生する可能性がある、過度に長い型を回避するためのショートカットとして。たとえば、次のメソッドシグネチャがあるとします。

public void doSomething(Foobar<String, Map<String, SomethingElse<Integer, Long>>>) { ... }

入力するのが面倒で面倒であり、さらに重要なことに、理解するのが困難です。代わりにこれを考慮してください:

public interface Widget extends Foobar<String, Map<String, SomethingElse<Integer, Long>>> { }

その後、メソッドは次のようになります。

public void doSomething(Widget widget) { ... }

ウィジェットがより明確になるだけでなく、ウィジェットインターフェースをJavadocできるようになり、ウィジェットのコード内のすべての出現箇所を検索するのも簡単になりました。

2)マーカーインターフェイスは、Javaの交差タイプの欠如を回避する方法としても使用できます。マーカーインターフェイスを使用すると、メソッドシグネチャなど、2つの異なるタイプのものを要求できます。上で説明したように、アプリケーションにインターフェースウィジェットがあるとします。ウィジェットを必要とするメソッドがあり、それをたまたまそれを反復させることができる場合(それは工夫されていますが、ここで私と一緒に動作します)、両方のインターフェイスを拡張するマーカーインターフェイスを作成することが唯一の良い解決策です。

public interface IterableWidget extends Iterable<String>, Widget { }

そしてあなたのコードで:

public void doSomething(IterableWidget widget) {
    for (String s : widget) { ... }
}

1
クラスがまたはを実装している場合、コンパイラはメソッドを作成しませ。クラスファイルを調べることで確認できます。さらに、「ショートカットインターフェイス」の作成、マーカーインターフェイスの目的ではありません。また、追加のインターフェースを満たすために追加の実装クラスを作成する必要があるため、これは実際に悪いコーディング手法です。ちなみに、Javaに 10年間、交差型があります。Genericsについて学ぶ…SerializableCloneable
Holger

クローン可能であれば、そのとおりです。Object.clone()の動作を変更します。それはまだ恐ろしいです。Josh Blochのリンクを参照してください 。メソッドに送信できるものを任意に制限するため、ショートカットインターフェイスは必ずしも良いデザインパターンではありません。同時に、やや制限が厳しいコードでも、より明確に書くことは通常妥当なトレードオフであると感じています。交差点の種類については、ジェネリックにのみ適用され、分類できないため、多くの場合役に立たない。シリアライズ可能であるインスタンス変数と、その他のインスタンス変数を用意してみてください。
MikeyB 2014

0

インターフェースにメソッドが含まれておらず、そのインターフェースを実装することにより、オブジェクトが何らかの能力を得る場合、そのようなタイプのインターフェースはマーカーインターフェースと呼ばれます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.