Generische Klassen
Teil des Abschnitts Objektorientierte Programmierung der Dart-Journey von Coddy — Lektion 65 von 110.
Sie haben gesehen, wie die integrierten Sammlungen von Dart wie List<String> Typparameter verwenden. Jetzt ist es an der Zeit, Ihre eigenen generischen Klassen zu erstellen, die mit jedem von Ihnen angegebenen Typ funktionieren.
Eine generische Klasse verwendet einen Typparameter (normalerweise T) als Platzhalter für den tatsächlichen Typ, der beim Erstellen einer Instanz angegeben wird:
class Box<T> {
T content;
Box(this.content);
T getContent() => content;
}
void main() {
var stringBox = Box<String>('Hello');
var intBox = Box<int>(42);
print(stringBox.getContent()); // Hello
print(intBox.getContent()); // 42
}Das T fungiert als Typvariable – wenn Sie Box<String> erstellen, wird jedes T in der Klasse zu String. Dies bietet Ihnen Typsicherheit, ohne separate Klassen für jeden Typ schreiben zu müssen.
Sie können mehrere Typparameter verwenden, wenn Ihre Klasse mit mehr als einem Typ arbeiten muss:
class Pair<K, V> {
K first;
V second;
Pair(this.first, this.second);
}
void main() {
var pair = Pair<String, int>('age', 25);
print('${pair.first}: ${pair.second}'); // age: 25
}Generische Klassen sind die Grundlage für wiederverwendbaren, typsicheren Code. Anstatt Logik für verschiedene Typen zu duplizieren oder Typinformationen mit dynamic zu verlieren, ermöglichen Generics das Schreiben von flexiblem Code, den der Compiler weiterhin überprüfen kann.
Aufgabe
EinfachLassen Sie uns ein Geschenkverpackungssystem bauen, das generische Klassen verwendet, um flexible Container für verschiedene Arten von Gegenständen zu erstellen. Sie werden eine generische Wrapper-Klasse erstellen, die jede Art von Inhalt verpacken kann, und eine GiftBox-Klasse mit zwei Typparametern, um ein Geschenk mit einer Grußkarte zu kombinieren.
Sie werden Ihren Code in zwei Dateien organisieren:
wrapper.dart: Definieren Sie hier Ihre generischen Klassen:- Eine generische Klasse
Wrapper<T>mit einer EigenschaftT contentund einem Konstruktor. Fügen Sie eine Methodeunwrap()hinzu, die den Inhalt zurückgibt, und eine Methodedescribe(), dieWrapped: [content]ausgibt. - Eine generische Klasse
GiftBox<G, C>mit zwei Eigenschaften:G giftundC card. Fügen Sie einen Konstruktor und eine Methodeopen()hinzu, dieGift: [gift]in einer Zeile undCard: [card]in der nächsten Zeile ausgibt.
- Eine generische Klasse
main.dart: Importieren Sie Ihre Wrapper-Datei und demonstrieren Sie die generischen Klassen mit verschiedenen Typen:- Erstellen Sie einen
Wrapper<String>, der'Chocolate'enthält, und rufen Siedescribe()auf. - Erstellen Sie einen
Wrapper<int>, der42enthält, und rufen Siedescribe()auf. - Erstellen Sie einen
Wrapper<double>, der3.14enthält, und rufen Siedescribe()auf. - Geben Sie eine Leerzeile aus.
- Erstellen Sie eine
GiftBox<String, String>mit dem Geschenk'Teddy Bear'und der Karte'Happy Birthday!', und rufen Sie dannopen()auf. - Geben Sie eine Leerzeile aus.
- Erstellen Sie eine
GiftBox<int, String>mit dem Geschenk100(das einen Geschenkkartenbetrag darstellt) und der Karte'Congratulations!', und rufen Sie dannopen()auf.
- Erstellen Sie einen
Beachten Sie, wie dieselbe Wrapper-Klasse nahtlos mit Strings, Integern und Doubles funktioniert. Die GiftBox-Klasse demonstriert die Verwendung von zwei Typparametern, um verschiedene Typen miteinander zu kombinieren – das Geschenk kann ein beliebiger Typ sein, während die Kartennachricht ein String bleibt.
Erwartete Ausgabe:
Wrapped: Chocolate
Wrapped: 42
Wrapped: 3.14
Gift: Teddy Bear
Card: Happy Birthday!
Gift: 100
Card: Congratulations!Spickzettel
Generische Klassen verwenden Typparameter (wie T) als Platzhalter für tatsächliche Typen, die beim Erstellen von Instanzen angegeben werden:
class Box<T> {
T content;
Box(this.content);
T getContent() => content;
}
void main() {
var stringBox = Box<String>('Hello');
var intBox = Box<int>(42);
print(stringBox.getContent()); // Hello
print(intBox.getContent()); // 42
}Wenn Sie Box<String> erstellen, wird jedes T in der Klasse zu String, was Typsicherheit bietet, ohne dass separate Klassen für jeden Typ erforderlich sind.
Verwenden Sie mehrere Typparameter, wenn Sie mit mehr als einem Typ arbeiten:
class Pair<K, V> {
K first;
V second;
Pair(this.first, this.second);
}
void main() {
var pair = Pair<String, int>('age', 25);
print('${pair.first}: ${pair.second}'); // age: 25
}Probier es selbst
import 'wrapper.dart';
void main() {
// TODO: Erstelle ein Wrapper<String>, das 'Chocolate' enthält, und rufe describe() auf
// TODO: Erstelle ein Wrapper<int>, das 42 enthält, und rufe describe() auf
// TODO: Erstelle ein Wrapper<double>, das 3.14 enthält, und rufe describe() auf
// TODO: Gib eine Leerzeile aus
// TODO: Erstelle eine GiftBox<String, String> mit dem Geschenk 'Teddy Bear'
// und der Karte 'Happy Birthday!', rufe dann open() auf
// TODO: Gib eine Leerzeile aus
// TODO: Erstelle eine GiftBox<int, String> mit dem Geschenk 100
// und der Karte 'Congratulations!', rufe dann open() 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