Marker-Interfaces in Java?

Mir wurde beigebracht, dass die Marker-Schnittstelle in Java eine leere Schnittstelle ist und dem Compiler oder JVM signalisiert wird, dass die Objekte der class, die diese Schnittstelle implementieren, in besonderer Weise behandelt werden müssen, wie zB Serialisierung, Klonen usw.

Aber in letzter Zeit habe ich gelernt, dass es eigentlich nichts mit dem Compiler oder der JVM zu tun hat. Im Fall der Serializable Schnittstelle führt die Methode writeObject(Object) von ObjectOutputStream instanceOf Serializable , um festzustellen, ob die class Serializable & implementiert und NotSerializableException entsprechend NotSerializableException . Alles wird im Code gehandhabt und dies scheint ein Design-Muster zu sein, so dass ich denke, dass wir unsere eigenen Marker-Interfaces definieren können.

Jetzt meine Zweifel:

  1. Ist die oben erwähnte Definition einer Marker-Schnittstelle im 1. Punkt falsch? Wie können wir dann eine Marker-Schnittstelle definieren?

  2. Und anstatt den instanceOf Operator zu verwenden, warum kann die Methode nicht so etwas wie writeObject(Serializable) so dass es eine Typüberprüfung der Kompilierungszeit statt der Laufzeit gibt?

  3. Wie sind Annotationen besser als Marker Interfaces?

   
    1. Ist die oben erwähnte Definition einer Marker-Schnittstelle im 1. Punkt falsch? – Es ist korrekt in den Teilen, dass (1) eine Markierungsschnittstelle leer sein muss und (2) ihre Implementierung eine spezielle Behandlung der ausführenden class implizieren soll. Der Teil, der falsch ist, besagt, dass JVM oder der Compiler die Objekte dieser class anders behandeln würden: Sie haben richtig erkannt, dass es der Code der Java-classnbibliothek ist, der diese Objekte als klonbar, serialisierbar usw. behandelt nichts mit dem Compiler oder der JVM zu tun.
    2. Anstatt den instanceOf-Operator zu verwenden, kann die Methode nicht so etwas wie writeObject(Serializable) so dass es eine Typüberprüfung zur Kompilierungszeit gibt – Dadurch können Sie Ihren Code nicht mit dem Namen der Marker-Schnittstelle verschmutzen, wenn ein “normales Object ” verwendet wird. wird gebraucht. Wenn Sie beispielsweise eine class erstellen, die serialisierbar sein muss und über Objektelemente verfügt, müssen Sie entweder ein Casting durchführen oder Ihre Objekte zur Kompilierzeit Serializable machen. Dies ist unbequem, da die Schnittstelle keine functionalität aufweist.
    3. Wie sind Annotationen besser als Marker Interfaces? – Sie ermöglichen es Ihnen, denselben Zweck zu erreichen, Metadaten an die Benutzer über die class zu übermitteln, ohne dafür einen eigenen Typ zu erstellen. Anmerkungen sind auch leistungsfähiger, sodass Programmierer komplexere Informationen an classn weitergeben können, die sie “konsumieren”.

    Es ist nicht möglich, Serializable für writeObject zu erzwingen, da writeObject einer nicht serialisierbaren class serialisierbar sein können, diese Instanzen jedoch in die übergeordnete class zurückübertragen werden können. Wenn Sie also einen Verweis auf etwas nicht serialisierbar halten (wie Object ), bedeutet das nicht, dass die referenzierte Instanz wirklich nicht serialisiert werden kann. Zum Beispiel in

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

    Die Elternklasse ( Object ) ist nicht serialisierbar und würde mit dem Konstruktor ohne Parameter initialisiert werden. Der mit x , String referenzierte Wert ist serialisierbar und die bedingte statement würde ausgeführt.

    Eine Marker-Schnittstelle in Java ist eine Schnittstelle ohne Felder oder Methoden. Einfach ausgedrückt, eine leere Schnittstelle in Java wird als Marker-Schnittstelle bezeichnet. Beispiele für Markierungsschnittstellen sind die Schnittstellen Serializable , Cloneable und Remote . Diese werden verwendet, um einige Informationen für Compiler oder JVMs anzuzeigen. Wenn die JVM also erkennt, dass eine class Serializable , kann sie eine spezielle Operation ausführen. Wenn die JVM sieht, dass eine class Cloneable implementiert, kann sie einige Operationen ausführen, um das Klonen zu unterstützen. Dasselbe gilt für RMI und die Remote Schnittstelle. Kurz gesagt, eine Markierungsschnittstelle zeigt dem Compiler oder der JVM ein Signal oder einen Befehl an.

    Das oben genannte begann als Kopie eines Blogposts , wurde aber leicht für die Grammatik bearbeitet.

    Die a / A-Marker-Schnittstelle, wie der Name andeutet, existiert nur, um etwas zu melden , das weiß, dass eine class etwas deklariert. Das alles können die JDK-classn für die Serializable Schnittstelle sein oder jede class, die Sie selbst für eine benutzerdefinierte class schreiben.

    b / Wenn es sich um eine Marker-Schnittstelle handelt, sollte dies nicht die Existenz einer Methode implizieren – es wäre besser, die implizierte Methode in die Schnittstelle aufzunehmen. Aber Sie können entscheiden, wie Sie es gestalten möchten, wenn Sie wissen, warum Sie es brauchen

    c / Es gibt wenig Unterschiede zwischen einer leeren Schnittstelle und einer Anmerkung, die keinen Wert oder Parameter verwendet. Aber der Unterschied besteht darin: Eine Annotation kann eine Liste von Schlüsseln / Werten deklarieren, auf die zur Laufzeit zugegriffen werden kann.

    ein. Ich habe sie immer als Designmuster und nichts als JVM-Special gesehen. Ich habe dieses Muster in verschiedenen Situationen verwendet.

    c. Ich glaube, dass die Verwendung von Anmerkungen zum Markieren von etwas eine bessere Lösung als die Verwendung von Markierungsschnittstellen ist. Ganz einfach, weil Schnittstellen in erster Linie darauf abzielen, gemeinsame Schnittstellen von Typen / classn zu definieren. Sie sind Teil der classnhierarchie.

    Annotationen sollen Meta-Informationen zum Code liefern, und ich denke, dass Marker Metainformationen sind. Sie sind also genau für diesen Anwendungsfall.

    1. Es hat nichts (unbedingt) mit der JVM und den Compilern zu tun, es hat etwas mit irgendeinem Code zu tun, der an einer bestimmten Markerschnittstelle interessiert ist und diese testet.

    2. Es ist eine Designentscheidung, und das aus gutem Grund. Siehe die Antwort von Audrius Meškauskas.

    3. In Bezug auf dieses spezielle Thema glaube ich nicht, dass es besser oder schlechter ist. Das Marker-Interface macht genau das, was es tun soll.

    Der Hauptzweck von Marker-Interfaces besteht darin, spezielle Typen zu erzeugen, bei denen die Typen selbst kein eigenes Verhalten haben.

     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); } 

    Hier stellt save-Methode sicher, dass nur die Objekte von classn gespeichert werden, die die MarkerEntity-Schnittstelle implementieren, für andere Typen wird InvalidEntityFoundException ausgetriggers. Hier definiert die MarkerEntity-Marker-Schnittstelle einen Typ, der den classn, die sie implementieren, ein spezielles Verhalten hinzufügt.

    Zwar können Annotationen jetzt auch verwendet werden, um classn für einige spezielle Behandlungen zu markieren, aber Marker-Annotationen sind Ersatz für Benennungsmuster, nicht für Marker-Interfaces.

    Markeranmerkungen können die Markerschnittstellen jedoch nicht vollständig ersetzen, weil; Marker-Interfaces werden verwendet, um den Typ zu definieren (wie oben bereits erläutert), wofür Marker-Annotationen nicht geeignet sind.

    Quelle für den Kommentar der Markierschnittstelle

    Ich würde zuerst argumentieren, dass Serializable und Clonable schlechte Beispiele für Marker-Interfaces sind. Sicher, sie sind Schnittstellen zu Methoden, aber sie implizieren Methoden wie writeObject(ObjectOutputStream) . (Der Compiler erstellt eine writeObject(ObjectOutputStream) -Methode für Sie, wenn Sie sie nicht überschreiben, und alle Objekte haben bereits clone() , aber der Compiler erstellt wieder eine echte clone() -Methode für Sie, aber mit Einschränkungen das sind seltsame Randfälle, die wirklich keine guten Designbeispiele sind.)

    Marker-Interfaces werden im Allgemeinen für einen von zwei Zwecken verwendet:

    1) Als Abkürzung, um einen zu langen Typ zu vermeiden, was bei vielen Generika vorkommen kann. Angenommen, Sie haben diese Methodensignatur:

     public void doSomething(Foobar>>) { ... } 

    Das ist chaotisch und nervig beim Tippen und, was noch wichtiger ist, schwer zu verstehen. Betrachten Sie dies stattdessen:

     public interface Widget extends Foobar>> { } 

    Dann sieht Ihre Methode so aus:

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

    Es ist nicht nur klarer, aber Sie können nun Javadoc die Widget-Oberfläche, und es ist auch einfacher, nach allen Vorkommen in Ihrem Code von Widget zu suchen.

    2) Marker-Schnittstellen können auch als Weg verwendet werden, um Javas fehlende Schnitttypen zu umgehen. Bei einer Marker-Schnittstelle können Sie festlegen, dass zwei verschiedene Typen vorhanden sind, z. B. in einer Methodensignatur. Angenommen, Sie haben ein Interface-Widget in Ihrer Anwendung, wie oben beschrieben. Wenn Sie eine Methode haben, die ein Widget benötigt, das Ihnen auch erlaubt, darüber zu iterieren (es ist erfunden, aber arbeiten Sie mit mir hier), ist Ihre einzige gute Lösung, eine Marker-Schnittstelle zu erstellen, die beide Schnittstellen erweitert:

     public interface IterableWidget extends Iterable, Widget { } 

    Und in deinem Code:

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

    Wenn eine Schnittstelle keine Methode enthält und diese Schnittstelle so implementiert wird, dass unser Objekt eine gewisse Fähigkeit erhält, werden diese Art von Schnittstellen als Marker-Oberflächen bezeichnet.

    Ich habe eine einfache Demonstration gemacht, um Zweifel Nr. 1 und 2 zu lösen:

    Wir werden eine Movable-Schnittstelle haben, die von der MobilePhone.java-class und einer weiteren class von LandlinePhone.java implementiert wird, die NICHT die Movable-Schnittstelle implementieren

    Unser Marker Interface:

     package com; public interface Movable { } 

    LandLinePhone.java und MobilePhone.java

      package com; class LandLinePhone { // more code here } class MobilePhone implements Movable { // more code here } 

    Unsere benutzerdefinierte Ausnahmeklasse: package com;

    Öffentliche class NotMovableException extends Exception {

     private static final long serialVersionUID = 1L; @Override public String getMessage() { return "this object is not movable"; } // more code here } 

    Unsere Testklasse: 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"); }} 

    Jetzt, wenn wir Hauptklasse ausführen:

     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) 

    Also egal welche class die Marker-Schnittstelle Movable implementiert, wird den Test bestehen, andernfalls wird eine Fehlermeldung angezeigt.

    So wird instanceOf eine Operatorüberprüfung für Serializable, Clonable etc