Konstruktor in einer Schnittstelle?

Lesezeit: 7 Minuten

Ich weiß, dass es nicht möglich ist, einen Konstruktor in einer Schnittstelle zu definieren. Aber ich frage mich warum, weil ich denke, dass es sehr nützlich sein könnte.

Sie könnten also sicher sein, dass einige Felder in einer Klasse für jede Implementierung dieser Schnittstelle definiert sind.

Betrachten Sie beispielsweise die folgende Nachrichtenklasse:

public class MyMessage {

   public MyMessage(String receiver) {
      this.receiver = receiver;
   }

   private String receiver;

   public void send() {
      //some implementation for sending the mssage to the receiver
   }
}

Wenn ich eine Schnittstelle für diese Klasse definiere, damit ich mehr Klassen haben kann, die die Nachrichtenschnittstelle implementieren, kann ich nur die Sendemethode und nicht den Konstruktor definieren. Wie kann ich also sicherstellen, dass jede Implementierung dieser Klasse wirklich einen Empfängersatz hat? Wenn ich eine Methode wie setReceiver(String receiver) Ich kann nicht sicher sein, ob diese Methode wirklich aufgerufen wird. Im Konstruktor konnte ich es sicherstellen.

  • mögliches Duplikat von Warum dürfen wir keinen Konstruktor in einer Schnittstelle angeben?

    – matt b

    10. Mai 2010 um 15:42 Uhr

  • Sie sagen: “Im Konstruktor könnte ich sicherstellen [every implementation of this class really has an receiver set].” Aber nein, das können Sie unmöglich tun. Vorausgesetzt, es wäre möglich, einen solchen Konstruktor zu definieren, wäre der Parameter nur ein starker Hinweis für Ihre Implementierer – aber sie könnten ihn einfach ignorieren, wenn sie wollten.

    – Julien Silland

    11. Mai 2010 um 3:08 Uhr

  • @mattb Ähm, das ist eine andere Sprache.

    – Yesennes

    23. Januar 2016 um 20:21 Uhr

  • Wenn Sie sicherstellen möchten, dass eine Klasse einen bestimmten Konstruktor hat B für einen funktionalen Zweck Perwägen Sie, dies zu verlangen P erhält die B als Parameter irgendwo. Auf diese Weise, je nachdem, wie Sie es deklarieren, (Supplier, BiFunction…), können Sie Verhalten garantieren. Sehen Sie sich als Beispiel diese Lösung an.

    – iamfrank

    19. April um 23:23 Uhr

Benutzer-Avatar
matt b

Nehmen Sie einige der Dinge, die Sie beschrieben haben:

“So konnte man sicher sein, dass einige Felder in einer Klasse für jede Implementierung dieser Schnittstelle definiert sind.”

„Wenn ich eine Schnittstelle für diese Klasse definiere, damit ich mehr Klassen haben kann, die die Nachrichtenschnittstelle implementieren, kann ich nur die Sendemethode und nicht den Konstruktor definieren.“

…diese Anforderungen sind genau das Richtige abstrakte Klassen sind für.

  • Beachten Sie jedoch, dass der von @Sebi beschriebene Anwendungsfall (Aufruf überladener Methoden von übergeordneten Konstruktoren) eine schlechte Idee ist, wie in meiner Antwort erläutert.

    – bzw

    10. Mai 2010 um 15:57 Uhr

  • Matt, das stimmt eindeutig, aber abstrakte Klassen leiden unter der Einschränkung der Einzelvererbung, was dazu führt, dass die Leute nach anderen Möglichkeiten suchen, Hierarchien anzugeben.

    – CPerkins

    10. Mai 2010 um 16:09 Uhr

  • Das ist wahr und könnte Sebis unmittelbares Problem lösen. Ein Grund für die Verwendung von Schnittstellen in Java ist jedoch, dass Sie keine Mehrfachvererbung haben können. In einem Fall, in dem ich mein “Ding” nicht zu einer abstrakten Klasse machen kann, weil ich von etwas anderem erben muss, bleibt das Problem bestehen. Nicht, dass ich behaupte, eine Lösung zu haben.

    – Jay

    10. Mai 2010 um 16:10 Uhr

  • @CPerkins Dies ist zwar wahr, aber ich schlage nicht vor, dass die einfache Verwendung einer abstrakten Klasse den Anwendungsfall von Sebi löst. Wenn überhaupt, ist es am besten, a zu deklarieren Message Schnittstelle, die die definiert send() -Methode, und wenn Sebi eine “Basis”-Klasse für Implementierungen der bereitstellen möchte Message Schnittstelle, dann stellen Sie eine bereit AbstractMessage auch. Abstrakte Klassen sollten Schnittstellen nicht ersetzen, ich habe nie versucht, dies vorzuschlagen.

    – matt b

    10. Mai 2010 um 16:17 Uhr

  • Verstanden, Matt. Ich habe nicht mit dir gestritten, sondern darauf hingewiesen, dass es keine ist Komplett Ersatz für das, was der Op will.

    – CPerkins

    10. Mai 2010 um 20:04 Uhr

Benutzer-Avatar
Daniel Kullmann

Ein Problem, das Sie bekommen, wenn Sie Konstruktoren in Interfaces zulassen, ergibt sich aus der Möglichkeit, mehrere Interfaces gleichzeitig zu implementieren. Wenn eine Klasse mehrere Schnittstellen implementiert, die verschiedene Konstruktoren definieren, müsste die Klasse mehrere Konstruktoren implementieren, von denen jeder nur eine Schnittstelle erfüllt, aber nicht die anderen. Es wird unmöglich sein, ein Objekt zu konstruieren, das jeden dieser Konstruktoren aufruft.

Oder im Code:

interface Named { Named(String name); }
interface HasList { HasList(List list); }

class A implements Named, HasList {

  /** implements Named constructor.
   * This constructor should not be used from outside, 
   * because List parameter is missing
   */
  public A(String name)  { 
    ...
  }

  /** implements HasList constructor.
   * This constructor should not be used from outside, 
   * because String parameter is missing
   */
  public A(List list) {
    ...
  }

  /** This is the constructor that we would actually 
   * need to satisfy both interfaces at the same time
   */ 
  public A(String name, List list) {
    this(name);
    // the next line is illegal; you can only call one other super constructor
    this(list); 
  }
}

  • Hätte die Sprache es nicht tun können, indem sie Dinge erlaubt hätte wie class A implements Named, HashList { A(){HashList(new list()); Named("name");} }

    – Mako

    10. September 2013 um 23:19 Uhr


  • Die nützlichste Bedeutung für einen “Konstruktor in einer Schnittstelle”, falls erlaubt, wäre if new Set<Fnord>() könnte so interpretiert werden: „Gib mir etwas, das ich verwenden kann Set<Fnord>“; wenn der Autor von Set<T> beabsichtigt HashSet<T> Um die Go-to-Implementierung für Dinge zu sein, die keinen besonderen Bedarf für etwas anderes hatten, könnte die Schnittstelle dann definieren new Set<Fnord>() könnte als Synonym angesehen werden new HashSet<Fnord>(). Für eine Klasse wäre es kein Problem, mehrere Schnittstellen zu implementieren, da new InterfaceName() würde einfach eine Klasse konstruieren von der Schnittstelle bezeichnet.

    – Superkatze

    10. Februar 2014 um 23:45 Uhr

  • Gegenargument: Ihr A(String,List) Konstruktor könnte der designierte Konstruktor sein, und A(String) und A(List) könnten sekundäre sein, die es nennen. Ihr Code ist kein Gegenbeispiel, nur ein schlechtes.

    – Ky.

    7. Februar 2018 um 3:02 Uhr

  • Warum würden Sie anrufen alle die Konstruktoren in einer Implementierung?! Ja, wenn es mehr Schnittstellen mit ctor implementiert, eine mit einem String und eine mit einem int, braucht es diese zwei ctors – aber entweder oder kann verwendet werden. Wenn dies nicht zutrifft, implementiert die Klasse einfach nicht beide Schnittstellen. Na und!? (Es gibt jedoch andere Gründe, ctor in Interfaces nicht zu haben).

    – Kai

    22. März 2019 um 12:52 Uhr


  • @kai Nein, beim Erstellen einer Instanz müssen beide Schnittstellenkonstruktoren aufgerufen werden. Mit anderen Worten: In meinem Beispiel hat die Instanz sowohl einen Namen als auch eine Liste, also muss jede Instanz sowohl den Namen als auch die Liste instanziieren.

    – Daniel Kullmann

    8. April 2019 um 15:59 Uhr

Eine Schnittstelle definiert einen Vertrag für eine API, d. h. eine Reihe von Methoden, auf die sich sowohl der Implementierer als auch der Benutzer der API einigen. Eine Schnittstelle hat keine instanzierte Implementierung, also keinen Konstruktor.

Der von Ihnen beschriebene Anwendungsfall ähnelt einer abstrakten Klasse, in der der Konstruktor eine Methode einer abstrakten Methode aufruft, die in einer untergeordneten Klasse implementiert ist.

Das inhärente Problem hierbei ist, dass während der Basiskonstruktor ausgeführt wird, das untergeordnete Objekt noch nicht konstruiert ist und sich daher in einem unvorhersehbaren Zustand befindet.

Zusammenfassend: Es ist problematisch, wenn Sie überladene Methoden von übergeordneten Konstruktoren aufrufen, um zu zitieren mindprod:

Im Allgemeinen müssen Sie es vermeiden, irgendwelche nicht-finalen Methoden in einem Konstruktor aufzurufen. Das Problem ist, dass Instanzinitialisierer / Variableninitialisierung in der abgeleiteten Klasse durchgeführt wird
nach der Konstruktor der Basisklasse.

Eine Problemumgehung, die Sie ausprobieren können, ist die Definition von a getInstance() -Methode in Ihrer Schnittstelle, damit der Implementierer weiß, welche Parameter behandelt werden müssen. Sie ist nicht so solide wie eine abstrakte Klasse, bietet aber mehr Flexibilität als eine Schnittstelle.

Diese Problemumgehung erfordert jedoch, dass Sie die verwenden getInstance() um alle Objekte dieser Schnittstelle zu instanziieren.

Z.B

public interface Module {
    Module getInstance(Receiver receiver);
}

Es gibt nur statische Felder in der Schnittstelle, die während der Objekterstellung in der Unterklasse nicht initialisiert werden müssen, und die Methode der Schnittstelle muss die tatsächliche Implementierung in der Unterklasse bereitstellen. Daher ist kein Konstruktor in der Schnittstelle erforderlich.

Zweiter Grund: Während der Objekterstellung der Unterklasse wird der übergeordnete Konstruktor aufgerufen. Wenn jedoch mehr als eine Schnittstelle implementiert wird, tritt beim Aufruf des Schnittstellenkonstruktors ein Konflikt darüber auf, welcher Schnittstellenkonstruktor zuerst aufgerufen wird

Benutzer-Avatar
Denys Vasylenko

Wenn Sie sicherstellen möchten, dass jede Implementierung der Schnittstelle bestimmte Felder enthält, können Sie einfach müssen Sie Ihrer Schnittstelle den Getter für dieses Feld hinzufügen:

interface IMyMessage(){
    @NonNull String getReceiver();
}
  • Es wird die Kapselung nicht brechen
  • Es wird allen, die Ihre Schnittstelle verwenden, mitteilen, dass die Receiver Objekt muss irgendwie an die Klasse übergeben werden (entweder per Konstruktor oder per Setter)

Benutzer-Avatar
Yishai

Abhängigkeiten, auf die nicht in Schnittstellenmethoden verwiesen wird, sollten als Implementierungsdetails betrachtet werden, nicht als etwas, das die Schnittstelle erzwingt. Natürlich kann es Ausnahmen geben, aber in der Regel sollten Sie Ihre Schnittstelle so definieren, wie das Verhalten erwartet wird. Der interne Zustand einer bestimmten Implementierung sollte kein Designproblem der Schnittstelle sein.

1344290cookie-checkKonstruktor in einer Schnittstelle?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy