it-swarm-eu.dev

Warum sollte eine statische verschachtelte Schnittstelle in Java verwendet werden?

Ich habe gerade eine statische verschachtelte Schnittstelle in unserer Codebasis gefunden.

class Foo {
    public static interface Bar {
        /* snip */
    }
    /* snip */
}

Ich habe das noch nie gesehen. Der ursprüngliche Entwickler ist nicht erreichbar. Deshalb muss ich SO fragen:

Welche Semantik steckt hinter einer statischen Schnittstelle? Was würde sich ändern, wenn ich static entferne? Warum sollte jemand das tun?

231
Mo.

Das Schlüsselwort static im obigen Beispiel ist redundant (eine verschachtelte Schnittstelle ist automatisch "statisch") und kann ohne Auswirkung auf die Semantik entfernt werden. Ich würde empfehlen, es zu entfernen. Gleiches gilt für "public" für Interface-Methoden und "public final" für Interface-Felder - die Modifikatoren sind redundant und fügen dem Quellcode nur Unordnung hinzu.

In jedem Fall deklariert der Entwickler einfach eine Schnittstelle mit dem Namen Foo.Bar. Es gibt keine weitere Verknüpfung mit der einschließenden Klasse, außer dass der Code, der nicht auf Foo zugreifen kann, auch nicht auf Foo.Bar zugreifen kann. (Aus dem Quellcode - Bytecode oder Reflektion kann auf Foo.Bar zugreifen, auch wenn Foo paketprivat ist!)

Es ist akzeptabel, eine verschachtelte Schnittstelle auf diese Weise zu erstellen, wenn Sie davon ausgehen, dass sie nur von der äußeren Klasse verwendet wird, sodass Sie keinen neuen Namen der obersten Ebene erstellen. Beispielsweise:

public class Foo {
    public interface Bar {
        void callback();
    }
    public static void registerCallback(Bar bar) {...}
}
// ...elsewhere...
Foo.registerCallback(new Foo.Bar() {
    public void callback() {...}
});
291
Jesse Glick

Die Frage wurde beantwortet, aber ein guter Grund für die Verwendung einer verschachtelten Schnittstelle ist, dass ihre Funktion in direktem Zusammenhang mit der Klasse steht, in der sie sich befindet. Ein gutes Beispiel hierfür ist Listener. Wenn Sie eine Klasse Foo haben und möchten, dass andere Klassen Ereignisse darauf abhören können, können Sie eine Schnittstelle mit dem Namen FooListener deklarieren, was in Ordnung ist, aber wahrscheinlich klarer eine verschachtelte Schnittstelle zu deklarieren und diese anderen Klassen Foo.Listener implementieren zu lassen (eine verschachtelte Klasse Foo.Event ist in diesem Zusammenhang nicht schlecht).

71
ColinD

Mitgliedsschnittstellen sind implizit statisch. Der statische Modifikator in Ihrem Beispiel kann entfernt werden, ohne die Semantik des Codes zu ändern. Siehe auch die Java Language Specification 8.5.1. Static Member Type Declarations

14
Bas Leijdekkers

Eine innere Schnittstelle muss statisch sein, damit auf sie zugegriffen werden kann. Die Schnittstelle ist nicht mit Instanzen der Klasse verknüpft, sondern mit der Klasse selbst, sodass mit Foo.Bar Wie folgt darauf zugegriffen werden kann:

public class Baz implements Foo.Bar {
   ...
}

In den meisten Fällen unterscheidet sich dies nicht von einer statischen inneren Klasse.

Die Antwort von Jesse ist nah, aber ich denke, dass es einen besseren Code gibt, um zu demonstrieren, warum eine innere Schnittstelle nützlich sein kann. Sehen Sie sich den folgenden Code an, bevor Sie weiterlesen. Finden Sie heraus, warum das innere Interface nützlich ist? Die Antwort ist, dass die Klasse DoSomethingAlready mit instanziiert werden kann irgendein Klasse, die A und C implementiert; nicht nur die konkrete Klasse Zoo. Natürlich kann dies auch dann erreicht werden, wenn AC nicht innerlich ist, aber stellen Sie sich vor, Sie könnten längere Namen (nicht nur A und C) verketten und dies für andere Kombinationen (z. B. A und B, C und B usw.) und Sie können dies problemlos tun sehen, wie die Dinge außer Kontrolle geraten. Ganz zu schweigen davon, dass die Leute, die Ihren Quelltext überprüfen, von Schnittstellen überfordert sind, die nur in einer Klasse von Bedeutung sind. eine innere Schnittstelle ermöglicht die Erstellung benutzerdefinierter Typen und verbessert deren Kapselung.

class ConcreteA implements A {
 :
}

class ConcreteB implements B {
 :
}

class ConcreteC implements C {
 :
}

class Zoo implements A, C {
 :
}

class DoSomethingAlready {
  interface AC extends A, C { }

  private final AC ac;

  DoSomethingAlready(AC ac) {
    this.ac = ac;
  }
}
6
user1982892

Um Ihre Frage direkt zu beantworten, schauen Sie auf Map.Entry.

Map.Entry

auch das kann nützlich sein

Static Nested Inerfaces Blog-Eintrag

3
Henry B

Im Jahr 1998 schlug Philip Wadler einen Unterschied zwischen statischen und nicht statischen Schnittstellen vor.

Soweit ich sehen kann, besteht der einzige Unterschied darin, dass eine Schnittstelle jetzt nicht-statische innere Klassen enthalten kann. Daher würde die Änderung keine vorhandenen Java Programme ungültig machen.

Zum Beispiel schlug er eine Lösung für das Ausdrucksproblem vor, bei der es sich um eine Nichtübereinstimmung zwischen Ausdruck als "Wie viel kann Ihre Sprache ausdrücken" einerseits und Ausdruck als "die Begriffe, in denen Sie zu vertreten versuchen" handelt Ihre Sprache "auf der anderen Seite.

Ein Beispiel für den Unterschied zwischen statischen und nicht statischen verschachtelten Schnittstellen finden Sie in seinem Beispielcode :

// This code does NOT compile
class LangF<This extends LangF<This>> {
    interface Visitor<R> {
        public R forNum(int n);
    }

    interface Exp {
        // since Exp is non-static, it can refer to the type bound to This
        public <R> R visit(This.Visitor<R> v);
    }
}

Sein Vorschlag hat es nie in Java 1.5.0 geschafft. Daher sind alle anderen Antworten korrekt: Es gibt keinen Unterschied zu statischen und nicht statischen verschachtelten Schnittstellen.

0
Pindatjuh

Wenn Sie die Klasse Foo in die Schnittstelle Foo ändern, ist das Schlüsselwort "public" im obigen Beispiel ebenfalls überflüssig, da

die in einer anderen Schnittstelle definierte Schnittstelle wird implizit öffentlich statisch

0
Danylo Volokh

Normalerweise sehe ich statische innere Klassen. Statische innere Klassen können nicht auf die enthaltenen Klassen verweisen, wohingegen nicht statische Klassen dies können. Es sei denn, Sie stoßen auf Paketkollisionen (es gibt bereits eine Schnittstelle namens Bar im selben Paket wie Foo). Ich denke, ich würde es zu einer eigenen Datei machen. Es könnte auch eine Entwurfsentscheidung sein, die logische Verbindung zwischen Foo und Bar zu erzwingen. Vielleicht wollte der Autor, dass Bar nur mit Foo verwendet wird (obwohl eine statische innere Schnittstelle dies nicht erzwingt, nur eine logische Verbindung).

0
basszero