Warum kann eine abstrakte Klasse, die eine Schnittstelle implementiert, die Deklaration / Implementierung einer der Methoden der Schnittstelle übersehen?

123

In Java passiert etwas Merkwürdiges, wenn Sie eine abstrakte Klasse zum Implementieren einer Schnittstelle verwenden: Einige der Methoden der Schnittstelle können vollständig fehlen (dh es ist weder eine abstrakte Deklaration noch eine tatsächliche Implementierung vorhanden), aber der Compiler beschwert sich nicht.

Zum Beispiel angesichts der Schnittstelle:

public interface IAnything {
  void m1();
  void m2();
  void m3();
}

Die folgende abstrakte Klasse wird ohne Warnung oder Fehler fröhlich kompiliert:

public abstract class AbstractThing implements IAnything {
  public void m1() {}
  public void m3() {}
}

Kannst du erklären warum?

Giulio Piancastelli
quelle
2
Man kann kein Objekt einer abstrakten Klasse erstellen. Solange für eine abstrakte Klasse keine Implementierung bereitgestellt wird, können für IAnything keine Objekte erstellt werden. Das ist also absolut in Ordnung für den Compiler. Der Compiler erwartet, dass jede nicht abstrakte Klasse, die IAnything implementiert, alle aus IAnything deklarierten Methoden implementieren muss. Und da man AbstractThing erweitern und implementieren muss, um Objekte erstellen zu können, gibt der Compiler einen Fehler aus, wenn diese Implementierung nicht die von AbstractThing ausgelassenen Methoden von IAnything implementiert.
VanagaS
Ich hatte eine konkrete Klasse, die ihr eigenes "AbstractThing" in einem identischen Szenario wie dieses erweiterte, und obwohl ich keine der Methoden in der Schnittstelle implementiert hatte, wurde sie unerklärlicherweise kompiliert. Jetzt macht es das, was ich erwartet habe, aber ich kann nicht herausfinden, warum es vorher erfolgreich war. Ich vermute, ich hatte keine :wder Dateien.
Braden Best
Sie können die Antwort für eine ähnliche Frage sehen stackoverflow.com/questions/8026580/…
Do Nhu Vy

Antworten:

155

Das liegt daran, dass Sie, wenn eine Klasse abstrakt ist, per Definition Unterklassen davon erstellen müssen, um sie zu instanziieren. Die Unterklassen müssen (vom Compiler) alle Schnittstellenmethoden implementieren, die die abstrakte Klasse ausgelassen hat.

Versuchen Sie nach Ihrem Beispielcode, eine Unterklasse zu erstellen, AbstractThingohne die m2Methode zu implementieren , und sehen Sie, welche Fehler der Compiler Ihnen gibt. Sie werden gezwungen, diese Methode zu implementieren.

Bill die Eidechse
quelle
1
Ich denke, der Compiler sollte immer noch Warnungen bezüglich abstrakter Klassen ausgeben, die Schnittstellen unvollständig implementieren, einfach weil Sie dann 2 Klassendefinitionen anstatt 1 durchgehen müssen, um zu sehen, was Sie in einer Unterklasse benötigen. Dies ist jedoch eine Einschränkung für Sprache / Compiler.
workmad3
3
Das wäre keine gute Idee, da es normalerweise viele abstrakte Klassen geben kann und die "falschen" Warnungen Sie bald überwältigen und Sie die "wahren" Warnungen verpassen würden. Wenn Sie darüber nachdenken, dient das Schlüsselwort 'abstract' speziell dazu, den Compiler anzuweisen, Warnungen für diese Klasse zu unterdrücken.
Belugabob
4
@workmad - Wenn Sie gemeinsame Implementierungen für eine Teilmenge von Schnittstellenmethoden haben, ist es sinnvoller, diese in eine separate Basisklasse zu zerlegen (DRY übertrumpft Ein-Ort-Code)
Gishu
4
Es wäre gefährlich zu verlangen , dass Sie leere Methodenimplementierungen in eine abstrakte Klasse einfügen. Wenn Sie dies tun würden, würden Implementierer von Unterklassen dieses Nichtverhalten erben, ohne dass der Compiler ihnen mitteilt, dass ein Problem vorliegt.
Bill the Lizard
8
Ich denke, was Workmad vorschlagen könnte, ist, dass Sie die Methoden in der abstrakten Klasse ohne einen Methodenkörper definieren und sie als abstrakt markieren. Kommt mir nicht schlecht vor.
Dónal
33

Perfekt in Ordnung.
Sie können abstrakte Klassen nicht instanziieren. Mit abstrakten Klassen können jedoch allgemeine Implementierungen für m1 () und m3 () untergebracht werden.
Also , wenn m2 () Implementierung ist für jede Implementierung aber m1 und m3 ist es nicht. Sie können mit nur der unterschiedlichen m2-Implementierung verschiedene konkrete IAnything-Implementierungen erstellen und von AbstractThing ableiten - unter Berücksichtigung des DRY-Prinzips. Die Überprüfung, ob die Schnittstelle für eine abstrakte Klasse vollständig implementiert ist, ist zwecklos.

Update : Interessanterweise finde ich, dass C # dies als Kompilierungsfehler erzwingt. In diesem Szenario müssen Sie die Methodensignaturen kopieren und ihnen in der abstrakten Basisklasse 'abstract public' voranstellen. (Jeden Tag etwas Neues :)

Gishu
quelle
7

Das ist gut. Um das Obige zu verstehen, müssen Sie zuerst die Natur der abstrakten Klassen verstehen. In dieser Hinsicht ähneln sie Schnittstellen. Dies ist , was Oracle sagt über diese hier .

Abstrakte Klassen ähneln Schnittstellen. Sie können sie nicht instanziieren und sie können eine Mischung von Methoden enthalten, die mit oder ohne Implementierung deklariert wurden.

Sie müssen sich also überlegen, was passiert, wenn eine Schnittstelle eine andere Schnittstelle erweitert. Zum Beispiel ...

//Filename: Sports.java
public interface Sports
{
   public void setHomeTeam(String name);
   public void setVisitingTeam(String name);
}

//Filename: Football.java
public interface Football extends Sports
{
   public void homeTeamScored(int points);
   public void visitingTeamScored(int points);
   public void endOfQuarter(int quarter);
}

... wie Sie sehen können, kompiliert dies auch einwandfrei. Einfach, weil eine Schnittstelle genau wie eine abstrakte Klasse NICHT instanziiert werden kann. Es ist daher nicht erforderlich, die Methoden von ihrem "Elternteil" explizit zu erwähnen. ALLE übergeordneten Methodensignaturen werden jedoch implizit Teil der erweiterten Schnittstelle oder der implementierenden abstrakten Klasse. Sobald eine geeignete Klasse (eine, die instanziiert werden kann) das oben Gesagte erweitert, muss sichergestellt werden, dass jede einzelne abstrakte Methode implementiert wird.

Hoffe das hilft ... und Allahu 'alam!

Dankbar
quelle
Das ist eine interessante Sichtweise. Ich denke, dass "abstrakte Klassen" wirklich "konkrete Schnittstellen" sind, dh Schnittstellen zu einigen konkreten Methoden, und nicht zu Klassen mit einigen abstrakten Methoden.
Giulio Piancastelli
... wirklich ein bisschen von beidem. Aber eines ist sicher, sie sind nicht instanziierbar.
Dankbar
4

Schnittstelle bedeutet eine Klasse, die keine Implementierung ihrer Methode hat, sondern nur eine Deklaration.
Andererseits ist eine abstrakte Klasse eine Klasse, die eine Implementierung einer Methode zusammen mit einer Methode mit nur einer Deklaration und keiner Implementierung haben kann.
Wenn wir eine Schnittstelle zu einer abstrakten Klasse implementieren, bedeutet dies, dass die abstrakte Klasse alle Methoden der Schnittstelle geerbt hat. Da es nicht wichtig ist, die gesamte Methode in einer abstrakten Klasse zu implementieren, handelt es sich jedoch um eine abstrakte Klasse (auch durch Vererbung), sodass die abstrakte Klasse einen Teil der Methode ohne Implementierung hier in der Schnittstelle belassen kann. Wenn diese abstrakte Klasse jedoch von einer konkreten Klasse geerbt wird, müssen sie alle diese nicht implementierten Methoden dort in der abstrakten Klasse implementieren.

Mustakimur Rahman
quelle
4

Angesichts der Schnittstelle:

public interface IAnything {
  int i;
  void m1();
  void m2();
  void m3();
}

So sieht es Java tatsächlich:

public interface IAnything {
  public static final int i;
  public abstract void m1();
  public abstract void m2();
  public abstract void m3();
}

Sie können also einige (oder alle) dieser abstractMethoden nicht implementieren, so wie Sie es bei abstractKlassen tun würden, die eine andere abstractKlasse erweitern.

Wenn Sie implementeine interface, gilt die Regel, dass alle interfaceMethoden in der abgeleiteten implementiert werden müssen class, nur für die konkrete classImplementierung (dh, die nicht abstractselbst ist).

Wenn Sie tatsächlich vorhaben, ein abstract classOut daraus zu erstellen , gibt es keine Regel, die besagt, dass Sie implementalle interfaceMethoden anwenden müssen (beachten Sie, dass in einem solchen Fall die Ableitung classals deklariert werden muss abstract).

Sharhp
quelle
Verwenden Sie javap IAnything.class, um das zweite Code-Snippet zu generieren.
Sharhp
3

Wenn eine abstrakte Klasse eine Schnittstelle implementiert

Im Abschnitt über Schnittstellen wurde festgestellt, dass eine Klasse, die eine Schnittstelle implementiert, alle Methoden der Schnittstelle implementieren muss. Es ist jedoch möglich, eine Klasse zu definieren, die nicht alle Methoden der Schnittstelle implementiert, vorausgesetzt, die Klasse wird als abstrakt deklariert. Beispielsweise,

abstract class X implements Y {   
    // implements all but one method of Y
}

class XX extends X {   
    // implements the remaining method in Y 
} 

In diesem Fall muss die Klasse X abstrakt sein, da sie Y nicht vollständig implementiert, die Klasse XX jedoch tatsächlich Y.

Referenz: http://docs.oracle.com/javase/tutorial/java/IandI/abstract.html

Mach Nhu Vy
quelle
1

Abstrakte Klassen sind nicht erforderlich, um die Methoden zu implementieren. Obwohl eine Schnittstelle implementiert wird, können die abstrakten Methoden der Schnittstelle abstrakt bleiben. Wenn Sie versuchen, eine Schnittstelle in einer konkreten Klasse zu implementieren (dh nicht abstrakt) und die abstrakten Methoden nicht implementieren, teilt Ihnen der Compiler Folgendes mit: Implementieren Sie entweder die abstrakten Methoden oder deklarieren Sie die Klasse als abstrakt.

Vincent Ramdhanie
quelle