Generische Constraints
Teil des Abschnitts Objektorientierte Programmierung der Dart-Journey von Coddy — Lektion 67 von 110.
Manchmal benötigen Sie einen generischen Typ, der über bestimmte Fähigkeiten verfügt. Wenn Sie beispielsweise Elemente vergleichen oder auf bestimmte Eigenschaften zugreifen möchten, garantiert ein uneingeschränktes T nicht, dass diese Funktionen vorhanden sind. Generische Einschränkungen ermöglichen es Ihnen, Typparameter auf bestimmte Typen oder deren Unterklassen zu beschränken.
Verwenden Sie das Schlüsselwort extends, um einen Typparameter einzuschränken:
class NumberBox<T extends num> {
T value;
NumberBox(this.value);
T doubled() => (value * 2) as T;
}
void main() {
var intBox = NumberBox<int>(5);
var doubleBox = NumberBox<double>(3.5);
print(intBox.doubled()); // 10
print(doubleBox.doubled()); // 7.0
// var stringBox = NumberBox<String>('hi'); // Fehler!
}Da T extends num, weiß der Compiler, dass value numerische Operationen wie die Multiplikation unterstützt. Ohne diese Einschränkung würde der Aufruf von value * 2 fehlschlagen.
Constraints funktionieren auch mit Ihren eigenen Klassenhierarchien:
abstract class Animal {
String get name;
}
class Dog extends Animal {
String get name => 'Dog';
}
class Shelter<T extends Animal> {
List<T> animals = [];
void add(T animal) => animals.add(animal);
void printNames() {
for (var animal in animals) {
print(animal.name); // Sicher: T garantiert, dass 'name' existiert
}
}
}Generische Constraints kombinieren die Flexibilität von Generics mit der Sicherheit zu wissen, welche Operationen für Ihren Typ verfügbar sind.
Aufgabe
EinfachLass uns einen Statistik-Rechner bauen, der nur mit numerischen Typen funktioniert! Du wirst eine generische Klasse mit einer Einschränkung erstellen, die sicherstellt, dass sie nur mit Zahlen verwendet werden kann, was es dir ermöglicht, mathematische Operationen sicher auf den gespeicherten Werten auszuführen.
Du wirst deinen Code in zwei Dateien organisieren:
stats.dart: Erstelle eine generischeStats<T extends num>-Klasse, die eine Liste von numerischen Werten verwaltet:- Eine
List<T>namensvalues, um die Zahlen zu speichern - Ein Konstruktor, der die Liste mit den bereitgestellten Werten initialisiert
- Eine Methode
sum(), die die Summe aller Werte alsnumzurückgibt - Eine Methode
min(), die den kleinsten Wert alsTzurückgibt - Eine Methode
max(), die den größten Wert alsTzurückgibt - Eine Methode
printStats(), welche die Statistiken formatiert anzeigt
- Eine
main.dart: Importiere deine Statistik-Datei und demonstriere die eingeschränkte generische Klasse mit verschiedenen numerischen Typen:- Erstelle ein
Stats<int>mit den Werten[10, 25, 5, 30, 15]und rufeprintStats()auf - Gib eine Leerzeile aus
- Erstelle ein
Stats<double>mit den Werten[3.5, 1.2, 4.8, 2.1]und rufeprintStats()auf
- Erstelle ein
Die Methode printStats() sollte drei Zeilen ausgeben, welche die Summe, das Minimum und das Maximum anzeigen. Da T extends num gilt, kannst du Vergleichsoperatoren und arithmetische Operationen sicher auf die Werte anwenden – etwas, das mit einem uneingeschränkten generischen Typ nicht möglich wäre.
Erwartete Ausgabe:
Sum: 85
Min: 5
Max: 30
Sum: 11.6
Min: 1.2
Max: 4.8Spickzettel
Generische Einschränkungen (Constraints) beschränken Typparameter auf bestimmte Typen oder deren Unterklassen unter Verwendung des Schlüsselworts extends:
class NumberBox<T extends num> {
T value;
NumberBox(this.value);
T doubled() => (value * 2) as T;
}Mit T extends num weiß der Compiler, dass value numerische Operationen wie die Multiplikation unterstützt.
Einschränkungen funktionieren auch mit benutzerdefinierten Klassenhierarchien:
abstract class Animal {
String get name;
}
class Shelter<T extends Animal> {
List<T> animals = [];
void printNames() {
for (var animal in animals) {
print(animal.name); // Sicher: T garantiert, dass 'name' existiert
}
}
}Generische Einschränkungen bieten Flexibilität und gewährleisten gleichzeitig Typsicherheit und verfügbare Operationen.
Probier es selbst
import 'stats.dart';
void main() {
// TODO: Erstelle ein Stats<int> mit den Werten [10, 25, 5, 30, 15]
// und rufe printStats() auf
// TODO: Drucke eine Leerzeile
// TODO: Erstelle ein Stats<double> mit den Werten [3.5, 1.2, 4.8, 2.1]
// und rufe printStats() 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