Menu
Coddy logo textTech

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.

challenge icon

Aufgabe

Einfach

Lass 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 generische Stats<T extends num>-Klasse, die eine Liste von numerischen Werten verwaltet:
    • Eine List<T> namens values, um die Zahlen zu speichern
    • Ein Konstruktor, der die Liste mit den bereitgestellten Werten initialisiert
    • Eine Methode sum(), die die Summe aller Werte als num zurückgibt
    • Eine Methode min(), die den kleinsten Wert als T zurückgibt
    • Eine Methode max(), die den größten Wert als T zurückgibt
    • Eine Methode printStats(), welche die Statistiken formatiert anzeigt
  • 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 rufe printStats() auf
    • Gib eine Leerzeile aus
    • Erstelle ein Stats<double> mit den Werten [3.5, 1.2, 4.8, 2.1] und rufe printStats() auf

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.8

Spickzettel

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
}
quiz iconTeste dich selbst

Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.

Alle Lektionen in Objektorientierte Programmierung