Erweitern oder Implementieren einer reinen abstrakten Klasse in TypeScript

76

Angenommen, ich habe eine reine abstrakte Klasse (dh eine abstrakte Klasse ohne Implementierung):

abstract class A {
    abstract m(): void;
}

Wie in C # und Java, kann ich verlängern die abstrakte Klasse:

class B extends A {
    m(): void { }
}

Aber anders als in C # und Java kann ich auch die abstrakte Klasse implementieren :

class C implements A {
    m(): void { }
}

Wie unterscheiden sich Klassen Bund CVerhaltensweisen? Warum sollte ich eins gegen das andere wählen?

(Derzeit decken das TypeScript- Handbuch und die Sprachspezifikation keine abstrakten Klassen ab.)

Michael Liu
quelle
1
Es gibt jetzt einen Abschnitt über abstrakte Klassen im TypeScript-Handbuch. typescriptlang.org/docs/handbook/classes.html Vielen Dank für diese Frage! Ich hatte keine Ahnung abstract, dass in TS verfügbar war, und dies half mir, es zu verstehen.
theUtherSide
Ziemlich erstaunlich, dass die von @theUtherSide verlinkten Dokumente irgendwie kein einziges Beispiel dafür haben class C implements A. Ich wusste nicht einmal, dass es möglich ist, bis ich diese Beiträge gelesen habe, und ich war in letzter Zeit viel in den ts-Dokumenten .
Kevin

Antworten:

98

Das Schlüsselwort implements behandelt die Klasse A als Schnittstelle, dh C muss alle in A definierten Methoden implementieren , unabhängig davon, ob sie in A implementiert sind oder nicht . Auch gibt es keine Anrufe zu Super Methoden in C .

Erweitert verhält sich eher so, wie Sie es vom Keyword erwarten würden. Sie müssen nur die abstrakten Methoden implementieren, und Superaufrufe sind verfügbar / generiert.

Ich denke, dass es bei abstrakten Methoden keinen Unterschied macht. Sie haben jedoch selten eine Klasse mit nur abstrakten Methoden. Wenn Sie dies tun, ist es viel besser, sie einfach in eine Schnittstelle umzuwandeln .

Sie können dies leicht sehen, indem Sie sich den generierten Code ansehen. Ich habe einen Spielplatz Beispiel hier .

toskv
quelle
12
Wenn Aes sich um eine reine abstrakte Klasse handelt, ist der einzige beobachtbare Unterschied möglicherweise das Verhalten des instanceofSchlüsselworts? Ich stimme zu, dass Schnittstellen wahrscheinlich reinen abstrakten Klassen vorzuziehen wären. In Angular 2 kann jedoch nur letzteres als DI-Provider-Token verwendet werden.
Michael Liu
1
In der Tat, danke, dass Sie die Instanz der Sache entdeckt haben, ich werde sie der Antwort zur Vervollständigung hinzufügen (Sie können eine Bearbeitung vornehmen, wenn Sie möchten). Was Angular2 betrifft, denke ich, dass dies ein Angular2-spezifisches Problem ist. :)
toskv
2
@toskv, danke, dass Sie die Anwendbarkeit des Schlüsselworts 'implementiert' auf Schnittstellen UND Klassen beschrieben haben. AngularJS 2.0 verwendet dies beispielsweise, um die 'OnInit'-KLASSE (anstelle der' Schnittstelle ') zu implementieren. Ich habe mich umgesehen, haben Sie einen Hinweis darauf. Ich sehe es nicht klar in der TS-Dokumentation.
MoMo
@toskv Die Referenz wurde in github @ github.com/Microsoft/TypeScript/blob/master/doc/spec.md#3.9.4 gefunden .
MoMo
18

Ich wurde hierher geführt, weil ich mir gerade die gleiche Frage gestellt hatte, und als ich die Antworten las, wurde mir klar, dass die Wahl auch den instanceofBediener betrifft .

Da eine abstrakte Klasse ein tatsächlicher Wert ist, der an JS ausgegeben wird, kann sie zur Laufzeitprüfung verwendet werden, wenn eine Unterklasse sie erweitert.

abstract class A {}

class B extends A {}

class C implements A {}

console.log(new B() instanceof A) // true
console.log(new C() instanceof A) // false
Tao
quelle
Danke @Tao das ist das reine und gut erklärte. Außerdem möchte ich zusätzliche Informationen für dieses hinzufügen, abtract oder nicht ist nicht wichtig, wenn Sie die abstrakten Schlüsselwörter entfernen, erhalten Sie das gleiche Ergebnis.
Dogan
10

Aufbauend auf @ toskv Antwort , wenn Sie erweitern eine abstrakte Klasse, müssen Sie rufen super()in der Konstruktor der Unterklasse. Wenn Sie die abstrakte Klasse implementieren , müssen Sie nicht aufrufen super()(aber Sie müssen alle in der abstrakten Klasse deklarierten Methoden implementieren, einschließlich privater Methoden).

Das Implementieren einer abstrakten Klasse anstelle einer Erweiterung kann hilfreich sein, wenn Sie eine Scheinklasse zum Testen erstellen möchten, ohne sich um die Abhängigkeiten und den Konstruktor der ursprünglichen Klasse kümmern zu müssen.

Cory
quelle
0

Im Beispiel der von Ihnen angegebenen Erweiterungen fügen Sie der Klasse nichts Neues hinzu. Es wird also um nichts erweitert. Obwohl es gültig ist, um nichts zu erweitern, scheint mir Typescript in diesem Fall "Geräte" angemessener zu sein. Aber am Ende des Tages sind sie gleichwertig.

Quentin 2
quelle