Marker-Schnittstellen in Java?


134

Mir wurde beigebracht, dass die Marker-Schnittstelle in Java eine leere Schnittstelle ist und dem Compiler oder der JVM signalisiert, dass die Objekte der Klasse, die diese Schnittstelle implementieren, auf besondere Weise behandelt werden müssen, z. B. Serialisieren, Klonen usw.

Aber in letzter Zeit habe ich gelernt, dass es eigentlich nichts mit dem Compiler oder der JVM zu tun hat. Zum Beispiel im Fall der SerializableSchnittstelle der Methode writeObject(Object)der ObjectOutputStreamtut , wie etwas instanceOf Serializablezu erkennen , ob die Klasse implementiert Serializableund führt NotSerializableExceptionentsprechend. Alles wird im Code behandelt und dies scheint ein Entwurfsmuster zu sein, so dass wir meiner Meinung nach unsere eigenen Markierungsschnittstellen definieren können.

Nun meine Zweifel:

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

  2. Und anstatt den instanceOfOperator zu verwenden, warum kann die Methode nicht writeObject(Serializable)so sein, dass eine Typprüfung zur Kompilierungszeit statt zur Laufzeit erfolgt?

  3. Wie sind Anmerkungen besser als Markierungsschnittstellen?


8
Serializableals Annotation ist Unsinn und @NonNullals Schnittstelle ist Unsinn. Ich würde sagen: Anmerkungen sind Marker + Metadaten. Übrigens: Forefunner of Annotations war XDoclet, geboren in Javadoc, getötet von Annotations.
Grimmiger

Antworten:


117
  1. Ist die oben im 1. Punkt erwähnte Definition einer Marker-Schnittstelle falsch? - In den Teilen ist es richtig, dass (1) eine Markierungsschnittstelle leer sein muss und (2) ihre Implementierung eine besondere Behandlung der implementierenden Klasse bedeuten soll. Der Teil, der falsch ist, impliziert, dass JVM oder der Compiler die Objekte dieser Klasse unterschiedlich behandeln würden: Sie stellen zu Recht fest, dass es der Code der Java-Klassenbibliothek ist, der diese Objekte als klonbar, serialisierbar usw. behandelt nichts mit dem Compiler oder der JVM zu tun.
  2. Anstatt den InstanzOf-Operator zu verwenden, kann die Methode nicht writeObject(Serializable)so sein, dass eine Typprüfung zur Kompilierungszeit erfolgt. Auf diese Weise können Sie vermeiden, dass Ihr Code mit dem Namen der Markierungsschnittstelle verschmutzt wird, wenn eine "Ebene Object" benötigt wird. Wenn Sie beispielsweise eine Klasse erstellen, die serialisierbar sein muss und über Objektelemente verfügt, müssen Sie Serializablebeim Kompilieren entweder Casting durchführen oder Ihre Objekte erstellen. Dies ist unpraktisch, da die Schnittstelle keine Funktionalität aufweist.
  3. Wie sind Anmerkungen besser als Markierungsschnittstellen? - Mit ihnen können Sie den gleichen Zweck erreichen, Metadaten über die Klasse an ihre Verbraucher zu übermitteln, ohne einen separaten Typ dafür zu erstellen. Anmerkungen sind ebenfalls leistungsfähiger, sodass Programmierer komplexere Informationen an Klassen weitergeben können, die sie "verbrauchen".

14
Ich habe es immer so verstanden, dass Annotationen eine Art 'Marker Interfaces 2.0' sind: Serialisierbar existiert seit Java 1.1, Annotationen wurden in 1.5 hinzugefügt
blagae

und weil writeObjectprivat ist, bedeutet das, dass zum Beispiel eine Klasse nicht die Implementierung der Superklasse aufrufen muss
Ratschenfreak

15
Beachten Sie, dass einige Jahre nach dem ursprünglichen Entwurf der Standardbibliothek Anmerkungen zur Sprache hinzugefügt wurden. Wenn Annotationen von Anfang an in der Sprache gewesen wären, wäre es zweifelhaft, dass Serializable eine Schnittstelle gewesen wäre, es wäre wahrscheinlich eine Annotation gewesen.
Theodore Norvell

Nun, im Falle Cloneableist nicht klar, ob es sich um eine Bibliothek oder eine JVM-Behandlung der Instanz handelt, die geändert wird.
Holger

"[...] ohne einen separaten Typ dafür zu erstellen." - Ich würde sagen , dass dies genau das ist , was sie trennt: Eine Marker - Schnittstelle macht eine Art einführen, während Anmerkungen (Art) does't.
Aioobe

22

Es ist nicht möglich zu erzwingen Serializableauf , writeObjectweil Kinder nicht serialisierbaren Klasse serialisierbar sein können, aber ihre Instanzen upcasted zurück auf die Elternklasse sein kann. Infolgedessen bedeutet das Halten eines Verweises auf etwas nicht serialisierbares (wie Object) nicht, dass die referenzierte Instanz nicht wirklich serialisiert werden kann. Zum Beispiel in

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

Die übergeordnete Klasse ( Object) ist nicht serialisierbar und würde mit ihrem parameterlosen Konstruktor initialisiert. Der Wert, auf den von ,, verwiesen wird x, Stringist serialisierbar und die bedingte Anweisung würde ausgeführt.


6

Eine Markierungsschnittstelle in Java ist eine Schnittstelle ohne Felder oder Methoden. Einfacher ausgedrückt wird eine leere Schnittstelle in Java als Markierungsschnittstelle bezeichnet. Beispiele für Marker - Schnittstellen sind die Serializable, Cloneableund RemoteSchnittstellen. Diese werden verwendet, um Compilern oder JVMs einige Informationen anzuzeigen. Wenn die JVM also Serializableerkennt, dass es sich um eine Klasse handelt , kann sie eine spezielle Operation ausführen. Wenn die JVM sieht, dass eine Klasse implementiert wird Cloneable, kann sie einige Vorgänge ausführen, um das Klonen zu unterstützen. Gleiches gilt für RMI und die RemoteSchnittstelle. Kurz gesagt, eine Markierungsschnittstelle zeigt dem Compiler oder der JVM ein Signal oder einen Befehl an.

Das Obige begann als Kopie eines Blogposts , wurde jedoch für die Grammatik leicht bearbeitet.


6
Das Kopieren ist in Ordnung, aber bitte geben Sie auch die Quelle an: javarevisited.blogspot.com/2012/01/… . Es ist auch schön, wenn Sie die Rechtschreibfehler nicht kopieren und einfügen. :)
Saurabh Patil

5

Eine / A-Markierungsschnittstelle, wie der Name schon sagt , existiert nur, um alles, was darüber weiß, darüber zu informieren , dass eine Klasse etwas deklariert. Das alles kann die JDK-Klasse für die SerializableSchnittstelle sein oder jede Klasse, die Sie selbst für eine benutzerdefinierte schreiben.

b / Wenn es sich um eine Markierungsschnittstelle handelt, sollte dies nicht die Existenz einer Methode implizieren. Es ist besser, die implizierte Methode in die Schnittstelle aufzunehmen. Sie können es jedoch nach Ihren Wünschen gestalten, wenn Sie wissen, warum Sie es benötigen

c / Es gibt kaum einen Unterschied zwischen einer leeren Schnittstelle und einer Anmerkung, die keinen Wert oder Parameter verwendet. Der Unterschied besteht jedoch darin, dass eine Anmerkung eine Liste von Schlüsseln / Werten deklarieren kann, auf die zur Laufzeit zugegriffen werden kann.


5

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

Wir werden eine bewegliche Schnittstelle haben, die von MobilePhone.javaClass implementiert wird, und eine weitere Klasse LandlinePhone.java, die KEINE bewegliche Schnittstelle implementiert

Unsere Marker-Oberfläche:

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;

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

Wenn wir nun die 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)

Unabhängig davon, welche Klasse die Markierungsschnittstelle implementiert, besteht Movablesie den Test. Andernfalls wird eine Fehlermeldung angezeigt.

Auf diese Weise wird die instanceOfBedienerprüfung auf Serializable , Cloneable usw. Durchgeführt


3

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

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

Anmerkungen zielen darauf ab, Code-Informationen für Code bereitzustellen, und ich denke, dass Marker Meta-Informationen sind. Sie sind also genau für diesen Anwendungsfall.


3
  1. Es hat (notwendigerweise) nichts mit der JVM und den Compilern zu tun, es hat etwas mit jedem Code zu tun, der an einer bestimmten Markierungsschnittstelle 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 denke ich nicht, dass es darum geht, besser oder schlechter zu sein. Die Marker-Oberfläche macht genau das, was sie soll.


Können Sie einen Link zu "der Antwort von Audrius Meškauskas" hinzufügen? Ich sehe auf dieser Seite nichts mit diesem Namen.
Sarah Messer

2

Der Hauptzweck von Markierungsschnittstellen besteht darin, spezielle Typen zu erstellen, bei denen die Typen selbst kein eigenes Verhalten aufweisen.

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 die Speichermethode sicher, dass nur die Objekte von Klassen gespeichert werden, die die MarkerEntity-Schnittstelle implementieren, für andere Typen wird InvalidEntityFoundException ausgelöst. Hier definiert die MarkerEntity-Markerschnittstelle einen Typ, der den Klassen, die ihn implementieren, ein besonderes Verhalten hinzufügt.

Obwohl Annotationen jetzt auch zum Markieren von Klassen für einige spezielle Behandlungen verwendet werden können, ersetzen Markierungsanmerkungen das Namensmuster und nicht die Markierungsschnittstellen.

Markierungsanmerkungen können die Markierungsschnittstellen jedoch nicht vollständig ersetzen, da: Markierungsschnittstellen werden verwendet, um den Typ zu definieren (wie oben bereits erläutert), während dies bei Markierungsanmerkungen nicht der Fall ist.

Quelle für Kommentar zur Markierungsschnittstelle


1
+1, um zu zeigen, dass es tatsächlich nur als "Sicherheitshaken" verwendet wird, von dem ein Programmierer ausdrücklich sagen muss, dass es in einer Datenbank gespeichert werden kann.
Ludvig W

1

Ich würde zunächst einmal argumentieren, dass Serializable und Cloneable 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 sie bereits clone(), aber der Compiler erstellt erneut eine echte clone()Methode für Sie, jedoch mit Einschränkungen. Beides sind seltsame Randfälle, die es wirklich nicht sind gute Designbeispiele.)

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

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

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

Das ist chaotisch und nervig zu tippen und vor allem schwer zu verstehen. Betrachten Sie stattdessen Folgendes:

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

Dann sieht Ihre Methode folgendermaßen aus:

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

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

2) Marker-Schnittstellen können auch verwendet werden, um das Fehlen von Schnittpunkttypen in Java zu umgehen. Bei einer Markierungsschnittstelle können zwei verschiedene Typen erforderlich sein, z. B. eine Methodensignatur. Angenommen, Sie haben ein Schnittstellen-Widget in Ihrer Anwendung, wie oben beschrieben. Wenn Sie eine Methode haben, für die ein Widget erforderlich ist, mit dem Sie auch darüber iterieren können (es ist erfunden, aber arbeiten Sie hier mit mir zusammen), besteht Ihre einzige gute Lösung darin, eine Markierungsschnittstelle zu erstellen, die beide Schnittstellen erweitert:

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

Und in Ihrem Code:

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

1
Der Compiler ist nicht erstellen alle Methoden , wenn Ihre Klasse implementiert Serializableoder Cloneable. Sie können dies überprüfen, indem Sie Ihre Klassendateien überprüfen. Darüber hinaus geht es beim Erstellen von „Shortcut-Schnittstellen“ nicht um Marker-Schnittstellen. Und es ist eine wirklich schlechte Codierungspraxis, da zusätzliche Implementierungsklassen erstellt werden müssten , um die zusätzlichen Schnittstellen zu erfüllen. Java hat übrigens seit einem Jahrzehnt Schnittpunkttypen. Erfahren Sie mehr über Generika…
Holger

Für klonbar hast du recht. Es ändert, was Object.clone () tut. Es ist immer noch schrecklich. Siehe Josh Bloch- Link Verknüpfungsschnittstellen sind nicht unbedingt ein gutes Entwurfsmuster, da Sie willkürlich einschränken, was Sie an eine Methode senden können. Gleichzeitig bin ich der Meinung, dass das Schreiben von klarerem, wenn auch etwas restriktiverem Code normalerweise ein vernünftiger Kompromiss ist. Kreuzungstypen gelten nur für Generika und sind nicht bezeichnbar und daher in vielen Fällen unbrauchbar. Versuchen Sie, eine Instanzvariable zu haben, die sowohl serialisierbar als auch alles andere ist.
MikeyB

0

Wenn eine Schnittstelle keine Methode enthält und durch Implementierung dieser Schnittstelle, wenn unser Objekt eine bestimmte Fähigkeit erhält, werden solche Schnittstellentypen als Markierungsschnittstellen bezeichnet.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.