Polymorphismus-Grundlagen
Teil des Abschnitts Objektorientierte Programmierung der Dart-Journey von Coddy — Lektion 57 von 110.
Polymorphismus bedeutet „viele Formen“ – es ist die Fähigkeit, Objekte verschiedener Klassen über eine gemeinsame Schnittstelle zu behandeln. Wenn Sie eine Referenz einer Elternklasse haben, kann diese auf jedes Objekt einer Kindklasse verweisen, und die korrekte Methode wird basierend auf dem tatsächlichen Objekttyp aufgerufen.
Das ist leistungsstark, da Sie Code schreiben können, der mit dem Elterntyp arbeitet und dennoch automatisch alle Kindtypen korrekt verarbeitet:
class Animal {
void speak() => print('Some sound');
}
class Dog extends Animal {
@override
void speak() => print('Woof!');
}
class Cat extends Animal {
@override
void speak() => print('Meow!');
}
void main() {
Animal myPet = Dog(); // Elterntyp, Kindobjekt
myPet.speak(); // Ausgabe: Woof!
myPet = Cat(); // Dieselbe Variable, anderes Objekt
myPet.speak(); // Ausgabe: Meow!
}Die Variable myPet ist als Animal deklariert, aber sie enthält ein Dog oder Cat. Wenn speak() aufgerufen wird, schaut Dart zur Laufzeit auf den tatsächlichen Objekttyp und ruft die entsprechende überschriebene Methode auf.
Dies ermöglicht es Ihnen, flexible Funktionen zu schreiben, die mit jeder Unterklasse funktionieren:
void makeAllSpeak(List<Animal> animals) {
for (var animal in animals) {
animal.speak(); // Jedes Tier spricht auf seine eigene Weise
}
}
void main() {
var pets = [Dog(), Cat(), Dog()];
makeAllSpeak(pets);
// Ausgabe: Woof! Meow! Woof!
}Die Funktion muss nichts über Dog oder Cat wissen – sie arbeitet einfach mit Animal. Dies macht Ihren Code erweiterbarer, da das Hinzufügen neuer Tierarten keine Änderungen an bestehenden Funktionen erfordert.
Aufgabe
EinfachLassen Sie uns ein Musikinstrumentensystem bauen, das Polymorphismus in Aktion demonstriert. Sie erstellen eine Basisklasse Instrument und verschiedene Instrumententypen und schreiben dann eine Funktion, die jede Sammlung von Instrumenten zusammen spielen lassen kann - ohne deren spezifische Typen zu kennen.
Sie werden Ihren Code in zwei Dateien organisieren:
instruments.dart: Definieren Sie hier Ihre Instrumentenhierarchie:- Eine
Instrument-Klasse mit einerString name-Eigenschaft und einem Konstruktor. Fügen Sie eine Methodeplay()hinzu, die'$name makes a sound'ausgibt. - Eine
Guitar-Klasse, dieInstrumenterweitert undplay()überschreibt, um'$name strums melodically'auszugeben. - Eine
Drum-Klasse, dieInstrumenterweitert undplay()überschreibt, um'$name beats rhythmically'auszugeben. - Eine
Piano-Klasse, dieInstrumenterweitert undplay()überschreibt, um'$name plays harmoniously'auszugeben.
performConcert(List<Instrument> instruments), die'Concert begins!'ausgibt, dann die Liste durchläuft undplay()für jedes Instrument aufruft und schließlich'Concert ends!'ausgibt.- Eine
main.dart: Importieren Sie Ihre Instrumentendatei und demonstrieren Sie Polymorphismus, indem Sie eine gemischte Band erstellen:- Erstellen Sie eine Liste vom Typ
List<Instrument>, die eineGuitarnamens'Acoustic', eineDrumnamens'Snare'und einPianonamens'Grand'enthält. - Übergeben Sie diese Liste an
performConcert().
- Erstellen Sie eine Liste vom Typ
Beachten Sie, wie performConcert() mit dem Elterntyp Instrument arbeitet, obwohl jedes Instrument auf seine eigene, einzigartige Weise spielt. Dies ist Polymorphismus - derselbe Methodenaufruf erzeugt unterschiedliches Verhalten basierend auf dem tatsächlichen Objekttyp.
Erwartete Ausgabe:
Concert begins!
Acoustic strums melodically
Snare beats rhythmically
Grand plays harmoniously
Concert ends!Spickzettel
Polymorphismus bedeutet "viele Formen" – er ermöglicht es Ihnen, Objekte verschiedener Klassen über eine gemeinsame Schnittstelle zu behandeln. Eine Referenz der Elternklasse kann auf jedes Objekt einer Kindklasse verweisen, und die korrekte Methode wird basierend auf dem tatsächlichen Objekttyp zur Laufzeit aufgerufen.
Grundlegendes Beispiel für Polymorphismus:
class Animal {
void speak() => print('Some sound');
}
class Dog extends Animal {
@override
void speak() => print('Woof!');
}
class Cat extends Animal {
@override
void speak() => print('Meow!');
}
void main() {
Animal myPet = Dog(); // Elterntyp, Kindobjekt
myPet.speak(); // Ausgabe: Woof!
myPet = Cat(); // Dieselbe Variable, anderes Objekt
myPet.speak(); // Ausgabe: Meow!
}Polymorphismus ermöglicht flexible Funktionen, die mit jeder Unterklasse funktionieren:
void makeAllSpeak(List<Animal> animals) {
for (var animal in animals) {
animal.speak(); // Jedes Tier spricht auf seine eigene Weise
}
}
void main() {
var pets = [Dog(), Cat(), Dog()];
makeAllSpeak(pets);
// Ausgabe: Woof! Meow! Woof!
}Die Funktion arbeitet mit dem Elterntyp Animal, ohne über spezifische Kindklassen wie Dog oder Cat Bescheid wissen zu müssen. Dies macht den Code erweiterbarer – das Hinzufügen neuer Typen erfordert keine Änderungen an bestehenden Funktionen.
Probier es selbst
import 'instruments.dart';
void main() {
// TODO: Erstelle eine List<Instrument>, die Folgendes enthält:
// - Eine Guitar namens 'Acoustic'
// - Eine Drum namens 'Snare'
// - Ein Piano namens 'Grand'
// TODO: Rufe performConcert() mit deiner Liste von Instrumenten auf
}
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Objektorientierte Programmierung
1Grundlagen der OOP
Externe DateienBibliotheken & ImportsEinführung in die OOPKlassen vs. ObjekteDas this-SchlüsselwortMethodenInstanzvariablenKonstruktor-GrundlagenWiederholung – Einfacher Taschenrechner4Null-Sicherheit
Einführung in Null-SicherheitNullable vs. Non-NullableDie ? und ! OperatorenLate-Schlüsselwort & Null-SicherheitNull-aware OperatorenNull-Sicherheit in KlassenZusammenfassung – Benutzerprofil-System7Abstrakte Klassen & Interfaces
Abstrakte KlassenAbstrakte MethodenInterfaces in DartImplizite InterfacesImplementieren vs. ErweiternMehrfache InterfacesRückblick – Shape Calculator10Collections & Generics
Überblick über List, Set und MapTypsichere CollectionsGenerische KlassenGenerische MethodenGenerische ConstraintsIterable & IteratorZusammenfassung – Generische Speicherung13Fortgeschrittene OOP-Konzepte
Komposition vs. VererbungExtension MethodsCallable ClassesSealed Classes (Dart 3)Records (Dart 3)Patterns & Matching (3.0)Enums mit Methoden16Projekt: Bibliotheksverwaltung
ProjektübersichtBuch- und Benutzerklassen2Konstruktoren in Dart
Standard-KonstruktorBenannte KonstruktorenInitialisierungslistenKonstante KonstruktorenFactory-KonstruktorenWeiterleitende KonstruktorenZusammenfassung – Shape Builder5Kapselung
Öffentliche vs. private MemberDie _ Präfix-KonventionSichtbarkeit auf Library-EbeneGetter & Setter im DetailInformation HidingRückblick – Schülerdatensätze8Mixins
Einführung in MixinsMixins erstellenMehrere Mixins verwendenDas on-Schlüsselwort in MixinsMixin vs. VererbungMixin vs. InterfaceZusammenfassung - Tiersystem11Spezielle Methoden
toString() überschreibenhashCode & == überschreibenComparable-Interfacecall()-MethodenoSuchMethod überschreibenZusammenfassung – Eigene Collection14Entwurfsmuster Teil 1
Einführung in EntwurfsmusterSingleton-MusterFactory-MusterObserver-MusterStrategy-Muster3Klasseneigenschaften
Instanz- vs. statische MemberFinal & Const FelderLate-VariablenStatische Methoden & FelderGetter und SetterZusammenfassung – Bankkonto-Manager6Vererbung
Grundlagen der VererbungDas super-SchlüsselwortMethoden überschreibenDie @override-AnnotationDas final-Klassen-SchlüsselwortKonstruktoren & VererbungRückblick – Mitarbeiter-Hierarchie9Polymorphismus
Polymorphismus-GrundlagenPolymorphismus über InterfacesTypprüfung zur LaufzeitDie is & as OperatorenDas covariant SchlüsselwortZusammenfassung – Payment Processor