JavaのMarkerインターフェイスは空のインターフェイスであり、このインターフェイスを実装するクラスのオブジェクトは、シリアル化、複製、等.
しかし、最近、それが実際にはコンパイラまたはJVMとは何の関係もないことを学びました。たとえば、Serializable
インターフェースの場合、ObjectOutputStream
のメソッドwriteObject(Object)
は、クラスがSerializable
を実装し、それに応じてNotSerializableException
をスローするかどうかを検出するためにinstanceOf Serializable
のようなことを行います。すべてがコードで処理され、これはデザインパターンのようであるため、独自のマーカーインターフェイスを定義できると思います。
今私の疑問:
1点目の上記のマーカーインターフェイスの定義は間違っていますか?それでは、マーカーインターフェイスをどのように定義できますか?
また、instanceOf
演算子を使用する代わりに、メソッドをwriteObject(Serializable)
のようにできないので、実行時ではなくコンパイル時の型チェックが行われるのはなぜですか?
注釈はマーカーインターフェイスよりも優れていますか?
writeObject(Serializable)
のようなものになり得ないため、コンパイル時の型チェックがあります-これにより、マーカーインターフェイスの名前でコードが汚染されないようにします。 「プレーンObject
」が必要です。たとえば、シリアライズ可能にする必要があり、オブジェクトメンバを持つクラスを作成する場合、コンパイル時にオブジェクトをキャストするか、変数Serializable
にする必要があります。インターフェースには機能がないため、これは不便です。Serializable
にwriteObject
を適用することはできません。なぜなら、シリアライズ不可能なクラスの子はシリアライズ可能ですが、インスタンスは親クラスにアップキャストされる可能性があるためです。その結果、シリアル化できないもの(Object
など)への参照を保持しても、参照されたインスタンスを実際にシリアル化できないということにはなりません。例えば
Object x = "abc";
if (x instanceof Serializable) {
}
親クラス(Object
)はシリアライズ可能ではなく、パラメーターなしのコンストラクターを使用して初期化されます。 x
によって参照される値String
はシリアル化可能であり、条件ステートメントが実行されます。
Javaのマーカーインターフェイスは、フィールドまたはメソッドのないインターフェイスです。簡単に言うと、Javaの空のインターフェイスはマーカーインターフェイスと呼ばれます。マーカーインターフェイスの例は、
Serializable
、Cloneable
、およびRemote
インターフェイスです。これらは、コンパイラまたはJVMにいくつかの情報を示すために使用されます。したがって、JVMがクラスがSerializable
であると認識すると、JVMはそのクラスに対して特別な操作を実行できます。同様に、JVMが何らかのクラスがCloneable
を実装していると判断した場合、クローニングをサポートするためにいくつかの操作を実行できます。 RMIおよびRemote
インターフェイスについても同様です。要するに、マーカーインターフェイスは、コンパイラまたはJVMへの信号またはコマンドを示します。
上記は ブログ投稿 のコピーとして始まりましたが、文法のために軽く編集されています。
a /その名前が示すように、マーカーインターフェイスは、それについて知っているものに通知するためにのみ存在しますクラスが何かを宣言すること。何でも、Serializable
インターフェースのJDKクラス、またはカスタムインターフェース用に自分で作成したクラスになります。
b /マーカーインターフェイスの場合、メソッドの存在を暗示してはなりません。インターフェイスに暗示されたメソッドを含めることをお勧めします。しかし、 why yo必要なことがわかっている場合は、必要に応じて設計することができます。
c /空のインターフェースと、値やパラメーターを使用しないアノテーションとの間にほとんど違いはありません。ただし、違いがあります。注釈は、実行時にアクセスできるキー/値のリストを宣言できます。
JVMとコンパイラーとは(必ずしも)関係ありません。特定のマーカーインターフェイスに関心があり、テストしているコードとは関係があります。
それは設計上の決定であり、正当な理由で行われています。 AudriusMeškauskasからの回答をご覧ください。
この特定のトピックに関しては、良くも悪くも問題ではないと思います。マーカーインターフェイスは、正常に機能するはずです。
a。私は常にそれらを設計パターンとして見てきましたが、JVM特別なものは何もありません。いくつかの状況でそのパターンを使用しました。
c。注釈を使用して何かをマークする方が、マーカーインターフェイスを使用するよりも優れたソリューションであると信じています。インターフェースがそもそもタイプ/クラスの共通インターフェースを定義することを目的としているためです。それらはクラス階層の一部です。
注釈はコードにメタ情報を提供することを目的としており、マーカーはメタ情報であると思います。したがって、それらはまさにそのユースケースに適しています。
マーカーインターフェイスの主な目的は、タイプ自体に独自の動作がない特別なタイプを作成することです。
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と2を解決するために簡単なデモを行いました:
MobilePhone.Javaクラスともう1つのクラスLandlinePhone.Javaによって実装されるMovableインターフェースを作成します。これらのクラスはNOT Movableインターフェースを実装します
マーカーインターフェイス:
package com;
public interface Movable {
}
LandLinePhone.JavaおよびMobilePhone.Java
package com;
class LandLinePhone {
// more code here
}
class MobilePhone implements Movable {
// more code here
}
カスタム例外クラス:package com;
パブリッククラス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がマーカーインターフェイスを実装するクラスはどれもテストに合格し、そうでない場合はエラーメッセージが表示されます。
これは、Serializable、CloneableなどのinstanceOf演算子チェックの方法です。
まず、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) { ... }
}
インターフェイスにメソッドが含まれておらず、オブジェクトが何らかの機能を取得する場合、そのインターフェイスを実装することにより、そのようなタイプのインターフェイスはマーカーインターフェイスと呼ばれます。