Ist "statische Schnittstelle" eine gute Praxis?


13

Ich habe erst kürzlich bemerkt, dass es eine Option gibt, statische Methoden in Schnittstellen zu haben. Wie bei statischen Schnittstellenfeldern gibt es ein interessantes Verhalten: Diese werden nicht vererbt.

Ich bin mir nicht sicher, ob es in den tatsächlichen Schnittstellen, die implementiert werden sollen, nützlich ist. Es ermöglicht dem Programmierer jedoch, Schnittstellen zu erstellen, die nur Hüllkurven für statische Inhalte sind, wie z. B. Utility-Klassen.

Ein einfaches Beispiel ist nur ein Umschlag für globale Konstanten. Im Vergleich zu einer Klasse können Sie das fehlende Boilerplate von leicht bemerken, public static finalda diese angenommen werden (was es weniger ausführlich macht).

public interface Constants {
    String LOG_MESSAGE_FAILURE = "I took an arrow to the knee.";
    int DEFAULT_TIMEOUT_MS = 30;
}

Sie könnten auch etwas komplexeres machen, wie diese Pseudo-Enumeration von Konfigurationsschlüsseln.

public interface ConfigKeys {
    static createValues(ConfigKey<?>... values) {
        return Collections.unmodifiableSet(new HashSet(Arrays.asList(values)));
    }

    static ConfigKey<T> key(Class<T> clazz) {
        return new ConfigKey<>(clazz);
    }

    class ConfigKey<T> {
        private final Class<T> type;
        private ConfigKey(Class<T> type) {
            this.type = type;
        }
        private Class<T> getType() {
            return type;
        }
    }
}

import static ConfigKeys.*;
public interface MyAppConfigKeys {
    ConfigKey<Boolean> TEST_MODE = key(Boolean.class);
    ConfigKey<String> COMPANY_NAME = key(String.class);

    Set<ConfigKey<?>> VALUES = createValues(TEST_MODE, COMPANY_VALUE);

    static values() {
        return VALUES;
    }
}

Sie können auf diese Weise auch eine Dienstprogramm- "Klasse" erstellen. In Dienstprogrammen ist es jedoch oft nützlich, private oder geschützte Hilfsmethoden zu verwenden, was in Klassen nicht ganz möglich ist.

Ich halte es für eine nette Neuerung, und insbesondere die Tatsache, dass die statischen Member nicht vererbt werden, ist ein interessantes Konzept, das nur für Schnittstellen eingeführt wurde.

Ich frage mich, ob Sie es für eine gute Praxis halten können. Während Code-Stil und Best Practices nicht selbstverständlich sind und es Raum für Meinungen gibt, gibt es meiner Meinung nach in der Regel gültige Gründe, die die Meinung stützen.

Ich interessiere mich mehr für die Gründe (nicht), Muster wie diese beiden zu verwenden.


Beachten Sie, dass ich diese Schnittstellen nicht implementieren möchte. Sie sind lediglich ein Umschlag für ihren statischen Inhalt. Ich beabsichtige nur, die Konstanten oder Methoden zu verwenden und möglicherweise den statischen Import zu verwenden.


1
Meine ruckartige Reaktion darauf ist, dass es nur zu schlechten Dingen führen kann. Obwohl öffentliche statische Methoden im Wesentlichen nur globale Funktionen mit Namespaces sind. Es fühlt sich an, als würde das Zulassen von Implementierungscode in einem Interface seinem Zweck als Vertragsdefinition ohne Implementierung widersprechen. Ich sage, überlasse die teilweise Implementierung den abstrakten Klassen. Ich werde nachlesen müssen, warum dies hinzugefügt wurde.
Adrian

Ich muss erst einmal gehen, aber ich werde morgen etwas darüber schreiben. Das kurze daran ist, dass zuvor ein Interface einen klaren Zweck hatte, der klar vom Zweck einer AbstractClass getrennt war. Jetzt überlappen sie sich stark in einer Weise, die die sprachunabhängige Definition eines Interfaces verletzt. Außerdem gibt es Fälle, in denen Sie nicht mehr von mehreren Schnittstellen erben können, wenn diese Standardmethoden implementieren. Daher gibt es jetzt Ausnahmen bei der Vererbung mehrerer Schnittstellen, bei denen es früher keine gab. Da Java drastisch von der gemeinsamen Definition einer Schnittstelle abgewichen ist ...
Adrian

Es könnte auch neue Entwickler verwirren, die versuchen, grundlegende OOP - Konzepte wie Interfaces, AbstractClases in den Griff zu bekommen, wenn Komposition statt Vererbung verwendet werden soll, ... usw.
Adrian

Aliester, Sie sprechen über defaultMethoden. Ich spreche über staticMethoden und Felder. Diese werden nicht vererbt, sodass sie die Mehrfachvererbung nicht aufheben.
Vlasec

Antworten:


1

Ein einfaches Beispiel ist nur ein Umschlag für globale Konstanten.

Das Setzen von Konstanten auf Schnittstellen wird als die bezeichnet, Constant Interface Antipatternda Konstanten häufig nur ein Implementierungsdetail sind. Weitere Nachteile sind im Wikipedia-Artikel über die Constant-Oberfläche zusammengefasst .

Sie können auf diese Weise auch eine Dienstprogramm- "Klasse" erstellen.

In der Java-Dokumentation heißt es in der Tat , dass statische Methoden das Organisieren von Hilfsmethoden erleichtern können, beispielsweise beim Aufrufen von Hilfsmethoden aus Standardmethoden.


1
Ich denke nicht, dass dies tatsächlich eine Antwort auf die gestellte Frage ist, da Sie diese beiden Dinge ohne diese neue Funktionalität tun könnten.
Adrian

Ah, also nach der Dokumentation ist es als Hilfsmethode gedacht. Es ist jedoch auch öffentlich, was dem widerspricht. Vielleicht soll es auch den Klassen helfen, die die Schnittstelle implementieren. Da sie jedoch nicht vererbt werden, müssten Sie statischen Import verwenden, damit es sich so anfühlt, als hätten Sie es vererbt. Vielen Dank, dass Sie in den Dokumenten nachgeschlagen haben.
Vlasec

2
Es ist absolut nichts Falsches daran, Konstanten in Schnittstellen einzufügen, wenn diese Konstanten irgendwie Teil der von der Schnittstelle beschriebenen API sind. Das einzige, was ein Antipattern ist, besteht darin, Konstanten in Interfaces ohne Methoden einzufügen und Klassen die Schnittstelle nur so implementieren zu lassen, dass sie auf die Konstanten ohne Klassenpräfix verweisen. Es ist ein Missbrauch von Schnittstellen und seit der Einführung von statischen Importen völlig veraltet.
Michael Borgwardt

1

Wie in der Frage angegeben, soll die Schnittstelle nicht implementiert (oder instanziiert) werden. Also könnten wir es mit einer Klasse vergleichen, die einen privaten Konstruktor hat:

  • Die Klasse kann nicht mit no super()to call erweitert werden, aber die Schnittstelle kann "implementiert" werden
  • Die Klasse kann nicht ohne Reflektionen instanziiert werden, während die Schnittstelle einfach als anonyme Klasse instanziiert werden kann: new MyInterface() {};

Dieser Vergleich ist zugunsten der Klasse, da er mehr Kontrolle ermöglicht. Das heißt, Klasse ist nicht dazu gedacht, statische Inhalte zu besitzen, man würde erwarten, dass es Instanzen gibt. Nun, es gibt keine perfekte Option.

Es gibt auch eine seltsame Sache bei der Vererbung von der "statischen Schnittstelle":

  • Eine "implementierende" Klasse erbt Felder, jedoch keine statischen Methoden
  • Eine Erweiterungsschnittstelle erbt überhaupt keine statischen Member
  • (Während eine Klasse, die eine Klasse erweitert, jedes nicht-private Mitglied erbt ... und da sie nicht durch eine Schnittstelle erweitert werden kann, gibt es weniger Raum für Verwirrung.)

Dies könnte Gründe dafür sein, es als schlechtes Muster zu betrachten und in diesen Fällen weiterhin statische Klassen zu verwenden. Für jemanden, der süchtig nach syntaktischem Zucker ist, kann es jedoch verlockend sein, auch mit den Nachteilen.


... Jedenfalls können die Kodierungskonventionen eines Teams diese Probleme abdecken. Obwohl es die Hand eines Programmierers nicht annähernd so fest zusammenhält wie eine Klasse mit einem privaten Konstruktor, birgt es für mich immer noch weniger Risiken als Setter, und Setter werden häufig verwendet.
Vlasec

0

Als @MarkusPscheidt ist diese Idee im Wesentlichen eine Erweiterung des Constant Interface Antipatterns. Dieser Ansatz wurde ursprünglich häufig verwendet, um die Vererbung eines einzelnen Satzes von Konstanten durch viele unterschiedliche Klassen zu ermöglichen. Der Grund dafür, dass dies letztendlich als Antimuster angesehen wurde, lag in den Problemen, die bei der Verwendung dieses Ansatzes in realen Projekten auftraten. Ich sehe keinen Grund zu der Annahme, dass sich diese Probleme nur auf Konstanten beziehen würden.

Wahrscheinlich noch wichtiger ist, dass es wirklich nicht nötig ist, dies zu tun. Verwenden Sie stattdessen statische Importe und geben Sie genau an, was Sie von wo importieren.


Ja, statische Importe klingen nach einer besseren Idee als die Vererbung von Konstanten und funktionieren mit statischen Mitgliedern von Klassen und Interfaces. Und es ist nur innerhalb der Klasse / Schnittstelle sichtbar.
Vlasec

@Vlasec Richtig, aber es ist nicht erforderlich, dies auf einer Schnittstelle zu tun. Der einzige Unterschied zwischen einer Klasse und einer Schnittstelle besteht darin, dass Sie von mehr als einer Schnittstelle erben können. Die Standardpraxis für eine Utility-Klasse wie diese besteht darin, den Konstruktor als privat zu kennzeichnen, um eine Instanziierung oder Erweiterung zu verhindern. Es hat keinen Vorteil, dies in einer Schnittstelle gegenüber einer Klasse zu tun. Es öffnet einfach die Tür für den Missbrauch.
JimmyJames

Ich kann Wert in Konstanten auf Schnittstellen sehen. Schnittstellen neigen dazu, Methoden zu definieren, und Methoden akzeptieren manchmal Konstanten als Parameter. Zum Beispiel: interface ColorFilter {Image applyGradient(int colorSpace, int width, byte[] pixels); }. Ich kann mir vorstellen , es gibt einige andere Konstanten zu sein RGB, RGBA, CMYK, GREYSCALE, INDEXEDusw.
Stijn de Witt

@StijndeWitt Dies wäre nicht das Schlimmste, aber Sie können für diesen Zweck eine Enumeration oder eine verschachtelte Klasse für die Schnittstelle verwenden. Das eigentliche Problem besteht darin, Statik durch Vererbung in den Bereich vieler Klassen zu bringen. Dieser Ansatz ist der Verwendung statischer Importe eindeutig unterlegen.
JimmyJames

0

Ich würde sagen, wenn Sie daran interessiert sind, wie die neue Funktion richtig verwendet wird, werfen Sie einen Blick auf den Code im JDK. Standardmethoden für Schnittstellen werden sehr häufig verwendet und sind leicht zu finden, statische Methoden für Schnittstellen scheinen jedoch sehr selten zu sein. Einige Beispiele hierfür sind java.time.chrono.Chronology, java.util.Comparator, java.util.Mapund schon einige Schnittstellen in java.util.functionund java.util.stream.

Bei den meisten Beispielen, die ich mir angesehen habe, handelt es sich um die Verwendung von APIs, die wahrscheinlich sehr häufig sind: Konvertierung zwischen verschiedenen Typen in der Zeit-API, zum Beispiel für Vergleiche, die wahrscheinlich häufig zwischen Objekten durchgeführt werden, die an Funktionen oder Objekte in übergeben werden Sammlungen. Zum Mitnehmen: Wenn ein Teil Ihrer API flexibel sein muss, Sie jedoch beispielsweise 80% der Anwendungsfälle mit einer Handvoll Standardeinstellungen abdecken können, sollten Sie statische Methoden für Ihre Schnittstellen in Betracht ziehen. Angesichts der Tatsache, dass diese Funktion im JDK anscheinend selten verwendet wird, wäre ich sehr konservativ, wenn ich sie in meinem eigenen Code verwende.

Ihre Beispiele sehen nicht so aus, wie ich es im JDK sehe. Für diese speziellen Fälle sollten Sie mit ziemlicher Sicherheit eine erstellen enum, die die gewünschte Schnittstelle implementiert. Eine Schnittstelle beschreibt eine Reihe von Verhaltensweisen und hat wirklich nichts damit zu tun, eine Sammlung ihrer Implementierungen zu verwalten.


-1

Ich frage mich, ob es Gründe gibt, es nicht zu benutzen.

Wie wäre es mit Kompatibilität mit älteren JVMs?

Halten Sie das im Allgemeinen für eine gute Idee?

Nein. Es ist eine rückwärts inkompatible Änderung, die die Ausdruckskraft der Sprache um ein Vielfaches erhöht. Zwei Daumen runter.

Gibt es Modellsituationen, in denen Sie Unterricht dringend empfehlen?

Ich empfehle dringend, Klassen zu verwenden, um Implementierungsdetails zu enthalten. Eine Schnittstelle soll eigentlich nur sagen, dass "dieses Objekt Operationen XYZ unterstützt", und das hat nichts mit statischen Methoden zu tun, die Sie möglicherweise in die Schnittstellendeklaration einfügen möchten. Diese Funktion ist wie eine Liege mit eingebautem Minikühlschrank. Sicher hat es einige Nischenverwendungen, aber es zieht nicht genug Gewicht, um den Mainstream zu verdienen.

UPDATE: Bitte keine Downvotes mehr. Offensichtlich denken einige Leute, dass statische Methoden in Schnittstellen die Knie der Biene sind, und ich kapituliere hiermit. Ich würde diese Antwort gleich löschen, aber die Kommentare im Anhang sind eine interessante Diskussion.


Kommentare sind nicht für längere Diskussionen gedacht. Diese Unterhaltung wurde in den Chat verschoben .
maple_shaft

Wenn ich Ihre Frage ablehnen würde, würde ich es tun, weil unsere ersten beiden Antworten keine wirklichen Antworten sind. Neue Sprachversionen bringen neue Tools, um die Arbeit zu vereinfachen. Genau das ist der Zweck dieser neuen Versionen. Die dritte Antwort ist ein bisschen verloren in den Trümmern der ersten beiden Busted.
Vlasec,
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.