web-dev-qa-db-ger.com

Optionale Methoden in der Java-Schnittstelle

Wenn Sie eine Schnittstelle in Java implementieren, müssen die in dieser Schnittstelle angegebenen Methoden von den Unterklassen verwendet werden, die diese Schnittstelle implementieren.

Ich habe festgestellt, dass es in einigen Schnittstellen wie der Collection-Schnittstelle Methoden gibt, die als optional kommentiert werden. Was genau bedeutet das? Es hat mich ein bisschen geworfen, als ich dachte, dass alle in der Schnittstelle angegebenen Methoden erforderlich wären?

99
mjsey

Die Antworten hier scheinen sehr verwirrend zu sein.

Die Sprache Java erfordert, dass jede Methode in einer Schnittstelle von jeder Implementierung dieser Schnittstelle implementiert wird. Zeitraum. Es gibt keine Ausnahmen von dieser Regel. Zu sagen, dass "Sammlungen eine Ausnahme sind" deutet auf ein sehr verschwommenes Verständnis dessen hin, was hier wirklich vor sich geht.

Es ist wichtig zu wissen, dass es zwei Ebenen für die Anpassung an eine Schnittstelle gibt:

  1. Welche Sprache kann die Java prüfen? Das läuft so ziemlich auf Folgendes hinaus: Gibt es eine Implementierung für jede der Methoden?

  2. Den Vertrag tatsächlich erfüllen. Funktioniert die Implementierung also so, wie es die Dokumentation in der Benutzeroberfläche vorschreibt?

    Gut geschriebene Schnittstellen enthalten eine Dokumentation, in der genau erklärt wird, was von den Implementierungen erwartet wird. Ihr Compiler kann dies nicht für Sie überprüfen. Sie müssen die Dokumente lesen und tun, was sie sagen. Wenn Sie nicht tun, was der Vertrag sagt, haben Sie eine Implementierung der Schnittstelle, was den Compiler betrifft, aber es wird eine fehlerhafte/ungültige Implementierung sein.

Als Joshua Bloch die Collections-API entwarf, entschied er, dass er anstatt sehr feinkörniger Schnittstellen zur Unterscheidung zwischen verschiedenen Varianten von Sammlungen (z. B. lesbar, beschreibbar, Direktzugriff usw.) nur sehr grobe Schnittstellen haben würde, und zwar in erster Linie Collection, List, Set und Map und dokumentieren dann bestimmte Vorgänge als "optional". Dies sollte die kombinatorische Explosion vermeiden, die sich aus feinkörnigen Grenzflächen ergeben würde. Aus dem Java Collections API Design FAQ :

Angenommen, Sie möchten der Hierarchie den Begriff der Modifizierbarkeit hinzufügen, um das Problem im Detail zu veranschaulichen. Sie benötigen vier neue Schnittstellen: ModifiableCollection, ModifiableSet, ModifiableList und ModifiableMap. Was früher eine einfache Hierarchie war, ist jetzt eine chaotische Heterarchie. Außerdem benötigen Sie eine neue Iterator-Schnittstelle für die Verwendung mit nicht modifizierbaren Sammlungen, die den Entfernungsvorgang nicht enthält. Können Sie UnsupportedOperationException jetzt abschaffen? Leider nicht.

Betrachten Sie Arrays. Sie implementieren die meisten Listenoperationen, entfernen und fügen sie jedoch nicht hinzu. Es handelt sich um Listen mit fester Größe. Wenn Sie diesen Begriff in der Hierarchie erfassen möchten, müssen Sie zwei neue Schnittstellen hinzufügen: VariableSizeList und VariableSizeMap. Sie müssen VariableSizeCollection und VariableSizeSet ​​nicht hinzufügen, da sie mit ModifiableCollection und ModifiableSet identisch sind. Sie können sie jedoch aus Gründen der Konsistenz trotzdem hinzufügen. Außerdem benötigen Sie eine neue ListIterator-Variante, die das Hinzufügen und Entfernen von Elementen nicht unterstützt. Jetzt haben wir bis zu zehn oder zwölf Schnittstellen sowie zwei neue Iterator-Schnittstellen anstelle unserer ursprünglichen vier. Sind wir fertig? Nein.

Berücksichtigen Sie Protokolle (z. B. Fehlerprotokolle, Überwachungsprotokolle und Journale für wiederherstellbare Datenobjekte). Hierbei handelt es sich um natürliche Anhängefolgen, die alle Listenoperationen mit Ausnahme von Entfernen und Setzen (Ersetzen) unterstützen. Sie erfordern eine neue Kernschnittstelle und einen neuen Iterator.

Und was ist mit unveränderlichen Sammlungen im Gegensatz zu nicht veränderbaren? (d. h. Sammlungen, die vom Client nicht geändert werden können UND sich aus keinem anderen Grund ändern). Viele argumentieren, dass dies die wichtigste Unterscheidung von allen ist, da es mehreren Threads ermöglicht, gleichzeitig auf eine Sammlung zuzugreifen, ohne dass eine Synchronisierung erforderlich ist. Um diese Unterstützung zur Typhierarchie hinzuzufügen, sind vier weitere Schnittstellen erforderlich.

Jetzt haben wir ungefähr zwanzig Schnittstellen und fünf Iteratoren, und es ist fast sicher, dass es in der Praxis immer noch Sammlungen gibt, die in keine der Schnittstellen passen. Beispielsweise handelt es sich bei den von Map zurückgegebenen Sammlungsansichten um reine Löschsammlungen. Es gibt auch Sammlungen, die bestimmte Elemente aufgrund ihres Werts ablehnen. Daher haben wir die Laufzeitausnahmen immer noch nicht beseitigt.

Letztendlich hielten wir es für einen soliden technischen Kompromiss, das gesamte Problem zu umgehen, indem eine sehr kleine Anzahl von Kernschnittstellen bereitgestellt wurde, die eine Laufzeitausnahme auslösen können.

Wenn Methoden in der Collections-API als "optionale Operationen" dokumentiert sind, bedeutet dies nicht, dass Sie die Methodenimplementierung einfach in der Implementierung auslassen oder einen leeren Methodenkörper verwenden können (zum einen viele) sie müssen ein Ergebnis zurückgeben). Es bedeutet vielmehr, dass eine gültige Implementierungsauswahl (eine, die noch dem Vertrag entspricht) darin besteht, ein UnsupportedOperationException zu werfen.

Da UnsupportedOperationException ein RuntimeException ist, können Sie ihn für den Compiler aus jeder Methodenimplementierung auslösen. Sie könnten es beispielsweise aus einer Implementierung von Collection.size() werfen. Eine solche Implementierung würde jedoch gegen den Vertrag verstoßen, da die Dokumentation für Collection.size() nicht besagt, dass dies zulässig ist.

Übrigens: Der von der Java-API Collections verwendete Ansatz ist etwas umstritten (wahrscheinlich jedoch weniger als zu Beginn der Einführung). In einer perfekten Welt hätten Schnittstellen keine optionalen Operationen, und stattdessen würden feinkörnige Schnittstellen verwendet. Das Problem ist, dass Java weder abgeleitete Strukturtypen noch Schnittstellentypen unterstützt, weshalb der Versuch, die Dinge "richtig" zu machen, im Fall von Sammlungen äußerst unhandlich wird.

217

Um eine implementierende (nicht abstrakte) Klasse für eine Schnittstelle zu kompilieren, müssen alle Methoden implementiert werden.

Jedoch, wenn wir an eine Methode denken, deren Implementierung eine einfache Ausnahme ist, die als "nicht implementiert" gilt (wie bei einigen Methoden in der Collection-Schnittstelle), dann ist die Collection-Schnittstelle die Ausnahme, nicht die reguläre Fall. Normalerweise, die implementierende Klasse sollte (und wird) alle Methoden implementieren.

Das "optional" in collection bedeutet, dass die implementierende Klasse sie (gemäß der obigen Terminologie) nicht "implementieren" muss, sondern nur NotSupportedException ) wirft. 

Ein gutes Beispiel - add() Methode für unveränderliche Sammlungen - Der Beton wird nur eine Methode implementieren, die nichts anderes macht als NotSupportedException

Im Fall von Collection wird verhindert, dass unordentliche Vererbungsbäume verhindert werden. Dies macht Programmierer unglücklich. In den meisten Fällen wird dieses Paradigma jedoch nicht empfohlen und sollte nach Möglichkeit vermieden werden.


Update:

Ab Java 8 wurde eine Standardmethode eingeführt.

Das heißt, eine Schnittstelle kann eine Methode definieren - einschließlich ihrer Implementierung .
Dies wurde hinzugefügt, um das Hinzufügen von Funktionen zu Schnittstellen zu ermöglichen, während die Abwärtskompatibilität für Codeabschnitte, die die neue Funktionalität nicht benötigen, weiterhin unterstützt wird.

Beachten Sie, dass die Methode weiterhin von allen Klassen implementiert wird, die sie deklarieren, jedoch die Definition der Schnittstelle verwendet.

25
amit

Eine Schnittstelle in Java erklärt lediglich den Vertrag zur Implementierung von Klassen. Alle Methoden in dieser Schnittstelle müssen implementiert werden, die implementierenden Klassen können jedoch nicht implementiert werden, d. H., Leer. Als ein erfundenes Beispiel

interface Foo {
  void doSomething();
  void doSomethingElse();
}

class MyClass implements Foo {
  public void doSomething() {
     /* All of my code goes here */
  }

  public void doSomethingElse() {
    // I leave this unimplemented
  }
}

Jetzt habe ich doSomethingElse() nicht implementiert, so dass meine Unterklassen sie implementieren können. Das ist optional.

class SubClass extends MyClass {
    @Override
    public void doSomethingElse() {
      // Here's my implementation. 
    }
}

Wenn Sie jedoch von Collection-Schnittstellen sprechen, sind dies, wie andere bereits gesagt haben, eine Ausnahme. Wenn bestimmte Methoden nicht implementiert werden und Sie diese aufrufen, können sie UnsupportedOperationException-Ausnahmen auslösen.

17
S.R.I

Die optionalen Methoden in der Collection-Schnittstelle bedeuten, dass die Implementierung der Methode eine Ausnahme auslösen darf, die jedoch trotzdem implementiert werden muss. Wie angegeben in den Dokumenten :

Für einige Erfassungsimplementierungen gelten Einschränkungen für die Elemente, die. sie können enthalten. Zum Beispiel verbieten einige Implementierungen null Elemente, und einige haben Einschränkungen bezüglich der Typen ihrer Elemente . Wenn Sie versuchen, ein nicht geeignetes Element hinzuzufügen, wird eine ungeprüfte Ausnahme ausgelöst In der Regel NullPointerException oder ClassCastException. Versuchen Das Vorhandensein eines unzulässigen Elements abfragen, kann eine Ausnahme auslösen, oder es kann einfach falsch zurückkehren; Bei einigen Implementierungen wird das .__ angezeigt. früheres Verhalten und einige werden das letztere zeigen. Allgemeiner, Versuch eines Vorgangs an einem unzulässigen Element, dessen Abschluss würde nicht dazu führen, dass ein unzulässiges Element in die .__ eingefügt wird. Collection kann eine Ausnahme auslösen oder mit der Option .__ erfolgreich sein. die Umsetzung. Solche Ausnahmen sind in .__ als "optional" markiert. Spezifikation für diese Schnittstelle.

16
MByD

Damit der Code kompiliert werden kann, müssen alle Methoden implementiert werden (abgesehen von denen mit default-Implementierungen in Java 8+). Die Implementierung muss jedoch nichts funktional Nützliches tun. Im Einzelnen:

  • Kann leer sein (eine leere Methode.)
  • Kann nur eine UnsupportedOperationException werfen (oder ähnlich)

Der letztere Ansatz wird häufig in den Collection-Klassen verwendet - alle Methoden sind noch implementiert, aber einige können eine Ausnahme auslösen, wenn sie zur Laufzeit aufgerufen werden.

9
Michael Berry

In Java 8 und höher ist die Antwort auf diese Frage immer noch gültig, aber jetzt differenzierter.

Zunächst bleiben diese Aussagen aus der akzeptierten Antwort richtig:

  • schnittstellen sollen ihr implizites Verhalten in einem Vertrag angeben (eine Anweisung von Verhaltensregeln, die das Implementieren von Klassen befolgen muss, um als gültig zu gelten)
  • es wird unterschieden zwischen Vertrag (Regeln) und Implementierung (programmatische Kodierung der Regeln)
  • methoden, die in der Schnittstelle angegeben sind, MÜSSEN IMMER implementiert werden (irgendwann)

Was ist also die Nuance, die in Java 8 neu ist? Wenn Sie von "Optionale Methoden" sprechen, dann ist eine der folgenden Möglichkeiten geeignet:

1. Eine Methode, deren Implementierung vertraglich optional ist

Die "dritte Anweisung" besagt, dass abstrakte Schnittstellenmethoden immer implementiert werden müssen. Dies gilt auch in Java 8+. Es ist jedoch wie im Java Collections Framework möglich, einige abstrakte Schnittstellenmethoden im Vertrag als "optional" zu bezeichnen.

In diesem Fall kann der Autor, der die Schnittstelle implementiert, die Methode nicht implementieren. Der Compiler besteht jedoch auf einer Implementierung. Daher verwendet der Autor diesen Code für optionale Methoden, die in der jeweiligen Implementierungsklasse nicht benötigt werden:

public SomeReturnType optionalInterfaceMethodA(...) {
    throw new UnsupportedOperationException();
}

In Java 7 und früheren Versionen war dies wirklich die einzige Art von "optionaler Methode", die es gab, d. H. Eine Methode, die, wenn sie nicht implementiert ist, eine UnsupportedOperationException ausgelöst hat. Dieses Verhalten wird notwendigerweise durch den Schnittstellenvertrag festgelegt (z. B. die optionalen Schnittstellenmethoden des Java Collections Framework).

2. Eine Standardmethode, deren Neuimplementierung optional ist

Java 8 führte das Konzept der default-Methoden ein. Dies sind Methoden, deren Implementierung von der Schnittstellendefinition selbst bereitgestellt werden kann und wird. Es ist im Allgemeinen nur dann möglich, Standardmethoden bereitzustellen, wenn der Methodenkörper unter Verwendung anderer Schnittstellenmethoden (d. H. Der "Grundelemente") geschrieben werden kann, und wenn this "Dieses Objekt bedeuten kann, dessen Klasse diese Schnittstelle implementiert hat".

Eine Standardmethode muss den Vertrag der Schnittstelle erfüllen (genau wie bei jeder anderen Implementierung der Schnittstellenmethode). Das Festlegen einer Implementierung der Schnittstellenmethode in einer implementierenden Klasse liegt daher im Ermessen des Autors (sofern das Verhalten seinem Zweck entspricht).

In dieser neuen Umgebung wurde das Java Collections Framework könnte umgeschrieben als:

public interface List<E> {
    :
    :
    default public boolean add(E element) {
        throw new UnsupportedOperationException();
    }
    :
    :
}

Auf diese Weise hat die "optionale" Methode add() das Standardverhalten, eine UnsupportedOperationException auszulösen, wenn die implementierende Klasse kein eigenes neues Verhalten liefert. Dies ist genau das, was Sie möchten und das mit dem Vertrag für List vereinbar ist. Wenn ein Autor eine Klasse schreibt, die das Hinzufügen neuer Elemente zu einer List-Implementierung nicht zulässt, ist die Implementierung von add() optional, da das Standardverhalten genau das ist, was benötigt wird.

In diesem Fall gilt die oben genannte "dritte Anweisung" weiterhin, da die Methode in der Schnittstelle selbst implementiert wurde. 

3. Eine Methode, die ein Optional-Ergebnis zurückgibt

Die letzte neue Art von optionaler Methode ist einfach eine Methode, die eine Optional zurückgibt. Die Optional-Klasse bietet einen entschieden objektiveren Umgang mit null-Ergebnissen.

Bei einem fließenden Programmierstil, wie er üblicherweise beim Codieren mit der neuen Java Streams-API zu sehen ist, führt ein Nullergebnis an einem beliebigen Punkt dazu, dass das Programm mit einer NullPointerException abstürzt. Die Optional-Klasse bietet einen Mechanismus zum Zurückgeben von NULL-Ergebnissen an den Clientcode auf eine Weise, die den fließenden Stil ermöglicht, ohne dass der Clientcode abstürzt.

4
scottb

Wenn wir den Code von AbstractCollection.Java in grepCode durchgehen, der für alle Sammlungsimplementierungen eine Vorfahrklasse ist, wird dies uns helfen, die Bedeutung optionaler Methoden zu verstehen. Hier ist der Code für die add (e) -Methode in der AbstractCollection-Klasse. Die Methode add (e) ist optional nach collection interface

public boolean  add(E e) {

        throw new UnsupportedOperationException();
    } 

Eine optionale Methode bedeutet, dass sie bereits in Vorfahrklassen implementiert ist und beim Aufruf eine UnsupportedOperationException auslöst. Wenn Sie unsere Sammlung modifizieren möchten, müssen Sie die Methoden optional in der Sammlungsschnittstelle überschreiben. 

4
IsAs

Tatsächlich bin ich von SurfaceView.Callback2 inspiriert. Ich denke, das ist der offizielle Weg

public class Foo {
    public interface Callback {
        public void requiredMethod1();
        public void requiredMethod2();
    }

    public interface CallbackExtended extends Callback {
        public void optionalMethod1();
        public void optionalMethod2();
    }

    private Callback mCallback;
}

Wenn Ihre Klasse keine optionalen Methoden implementieren muss, "implementiere Callback" . Wenn Ihre Klasse optionale Methoden implementieren muss, "implementiert CallbackExtended".

Entschuldigung für Scheiße Englisch.

3
Wonson

Nun, dieses Thema wurde an ... yeah gerichtet, aber denken Sie, eine Antwort fehlt. Ich spreche von den "Standardmethoden" von Interfaces . Nehmen wir zum Beispiel an, Sie haben eine Klasse zum Schließen von Dingen (wie einem Destruktor oder so etwas). Nehmen wir an, es sollte 3 Methoden haben. Nennen wir sie "doFirst ()", "doLast ()" und "onClose ()".

Wir sagen also, dass jedes Objekt dieses Typs mindestens "onClose ()" erkennen soll, die anderen sind jedoch optional.

Sie können dies mithilfe der "Standardmethoden" der Schnittstellen realisieren. Ich weiß, dies würde den Grund für eine Schnittstelle meistens negieren, aber wenn Sie ein Framework entwerfen, kann dies nützlich sein.

Wenn Sie es also auf diese Weise realisieren wollen, würde es folgendermaßen aussehen

public interface Closer {
    default void doFirst() {
        System.out.print("first ... ");
    }
    void onClose();
    default void doLast() {
        System.out.println("and finally!");
    }
}

Was jetzt passieren würde, wenn Sie es zum Beispiel in einer Klasse namens "Test" implementieren würden, wäre der Compiler mit dem Folgenden vollkommen in Ordnung:

public class TestCloser implements Closer {
    @Override
    public void onClose() {
        System.out.print("closing ... ");
    }
}

mit der Ausgabe:

first ... closing ... and finally!

oder

public class TestCloser implements Closer {
    @Override
    public void onClose() {
        System.out.print("closing ... ");
    }

    @Override
    public void doLast() {
        System.out.println("done!");
    }
}

mit der Ausgabe:

first ... closing ... done!

Alle Kombinationen sind möglich. Alles, was "default" ist, kann implementiert werden, muss aber nicht implementiert werden.

Ich hoffe, es ist nicht völlig falsch, dass ich jetzt antworte.

Ich wünsche euch allen einen schönen Tag!

[edit1]: Bitte beachten Sie: Dies funktioniert nur in Java 8.

3
Thorben Kuck

Oracle Java Collections Tutorial:

Um die Anzahl der Core-Collection-Schnittstellen überschaubar zu halten, bietet die Java-Plattform keine separaten Schnittstellen für jede Variante jedes Collection-Typs. (Zu diesen Varianten gehören möglicherweise unveränderliche, feste Größe und nur Anhänge.) Stattdessen werden die Änderungsoperationen in jeder Schnittstelle als optional gekennzeichnet - eine bestimmte Implementierung kann nicht alle Operationen unterstützen. Wenn eine nicht unterstützte Operation aufgerufen wird, löst eine Collection eine UnsupportedOperationException aus. Implementierungen sind dafür verantwortlich, zu dokumentieren, welche der optionalen Operationen sie unterstützen. Alle Universalimplementierungen der Java-Plattform unterstützen alle optionalen Vorgänge.

0
Trent Steele

Ich suchte nach einer Möglichkeit, die Rückrufschnittstelle zu implementieren. Daher war die Implementierung optionaler Methoden erforderlich, da ich nicht jede Methode für jeden Rückruf implementieren wollte. 

Anstatt eine Schnittstelle zu verwenden, verwendete ich eine Klasse mit leerer Implementierung wie:

public class MyCallBack{
    public void didResponseCameBack(String response){}
}

Und Sie können die Member-Variable CallBack so setzen, 

c.setCallBack(new MyCallBack() {
    public void didResponseCameBack(String response) {
        //your implementation here
    }
});

dann nenne es so.

if(mMyCallBack != null) {
    mMyCallBack.didResponseCameBack(response);
}

Auf diese Weise müssen Sie sich nicht darum kümmern, jede Methode pro Rückruf zu implementieren, sondern überschreiben Sie nur die Methoden, die Sie benötigen.

0
green0range

Obwohl die Frage des OP nicht beantwortet wird, ist es erwähnenswert, dass das Hinzufügen von Standardmethoden zu den Schnittstellen ab Java 8 tatsächlich machbar ist . Das default-Schlüsselwort, das in der Methodensignatur einer Schnittstelle enthalten ist, führt dazu, dass eine Klasse die Möglichkeit hat, die Methode zu überschreiben, dies jedoch nicht erfordert.

0
Troy Stopera