hashCode & == überschreiben
Teil des Abschnitts Objektorientierte Programmierung der Dart-Journey von Coddy — Lektion 71 von 110.
Standardmäßig sind zwei Objekte in Dart nur dann gleich, wenn sie genau dieselbe Instanz im Speicher sind. Das bedeutet, dass zwei Objekte mit identischen Werten immer noch als unterschiedlich betrachtet werden:
class Point {
int x, y;
Point(this.x, this.y);
}
void main() {
var p1 = Point(3, 4);
var p2 = Point(3, 4);
print(p1 == p2); // false - unterschiedliche Instanzen!
}Um Objekte anhand ihrer Werte zu vergleichen, müssen Sie sowohl den ==-Operator als auch hashCode überschreiben. Diese beiden müssen immer zusammen überschrieben werden – wenn zwei Objekte gleich sind, müssen sie denselben Hash-Code haben:
class Point {
final int x, y;
Point(this.x, this.y);
@override
bool operator ==(Object other) {
if (other is! Point) return false;
return x == other.x && y == other.y;
}
@override
int get hashCode => Object.hash(x, y);
}
void main() {
var p1 = Point(3, 4);
var p2 = Point(3, 4);
print(p1 == p2); // true - gleiche Werte!
}Der hashCode ist entscheidend für Sammlungen wie Set- und Map-Schlüssel. Darts Hilfsfunktion Object.hash() kombiniert mehrere Werte zu einem einzigen Hash-Code. Ohne einen ordnungsgemäßen hashCode funktionieren Ihre Objekte in Hash-basierten Sammlungen nicht korrekt, selbst wenn == überschrieben wurde.
Aufgabe
EinfachLassen Sie uns ein Koordinatensystem bauen, in dem Punkte nach ihren Werten und nicht nach ihren Speicheradressen verglichen werden können! Sie werden eine Coordinate-Klasse erstellen, die wertbasierte Gleichheit ordnungsgemäß implementiert, sodass sie korrekt mit Gleichheitsprüfungen und hash-basierten Sammlungen wie Set funktioniert.
Sie werden Ihren Code in zwei Dateien organisieren:
coordinate.dart: Erstellen Sie eineCoordinate-Klasse, die einen Punkt im 2D-Raum mit den Feldernfinal int xundfinal int ydarstellt. Überschreiben Sie sowohl den==-Operator als auchhashCode, sodass zwei Koordinaten mit denselben x- und y-Werten als gleich betrachtet werden. Verwenden SieObject.hash(), um den Hash-Code aus beiden Feldern zu generieren.main.dart: Importieren Sie Ihre Koordinatendatei und demonstrieren Sie, wie wertbasierte Gleichheit das Verhalten beim Objektvergleich verändert:- Erstellen Sie zwei separate
Coordinate-Objekte, beide mit x:5und y:10 - Geben Sie aus, ob sie mit
==gleich sind - Geben Sie aus, ob sie identisch sind (dieselbe Instanz), indem Sie
identical()verwenden - Erstellen Sie ein
Set<Coordinate>und fügen Sie beide Koordinaten hinzu - Geben Sie die Länge des Sets aus (sie sollte 1 sein, da sie gleich sind!)
- Erstellen Sie eine dritte Koordinate mit x:
3und y:7, und fügen Sie diese dem Set hinzu - Geben Sie die endgültige Länge des Sets aus
- Erstellen Sie zwei separate
Diese Herausforderung zeigt, warum hashCode und == immer zusammen überschrieben werden müssen. Das Set verwendet Hash-Codes, um Elemente zu organisieren. Ohne einen korrekten hashCode könnten selbst gleiche Objekte als Duplikate gespeichert werden!
Erwartete Ausgabe:
true
false
1
2Spickzettel
Standardmäßig sind zwei Objekte in Dart nur dann gleich, wenn sie dieselbe Instanz im Speicher sind, nicht wenn sie dieselben Werte haben:
var p1 = Point(3, 4);
var p2 = Point(3, 4);
print(p1 == p2); // false - unterschiedliche Instanzen!Um Objekte anhand ihrer Werte zu vergleichen, überschreiben Sie sowohl den ==-Operator als auch hashCode. Diese müssen immer zusammen überschrieben werden:
class Point {
final int x, y;
Point(this.x, this.y);
@override
bool operator ==(Object other) {
if (other is! Point) return false;
return x == other.x && y == other.y;
}
@override
int get hashCode => Object.hash(x, y);
}Verwenden Sie Object.hash(), um mehrere Werte zu einem einzigen Hash-Code zu kombinieren. Der hashCode ist entscheidend für Collections wie Set und Map – ohne ihn funktionieren gleiche Objekte in Hash-basierten Collections nicht korrekt.
Um zu prüfen, ob zwei Objekte dieselbe Instanz sind (nicht nur gleiche Werte), verwenden Sie identical():
print(identical(p1, p2)); // false - unterschiedliche InstanzenProbier es selbst
import 'coordinate.dart';
void main() {
// TODO: Erstelle zwei separate Coordinate-Objekte, beide mit x: 5 und y: 10
// TODO: Gib aus, ob sie gleich sind, unter Verwendung von ==
// TODO: Gib aus, ob sie identisch sind (dieselbe Instanz), unter Verwendung von identical()
// TODO: Erstelle ein Set<Coordinate> und füge beide Koordinaten hinzu
// TODO: Gib die Länge des Sets aus (sollte 1 sein, da sie gleich sind!)
// TODO: Erstelle eine dritte Koordinate mit x: 3 und y: 7 und füge sie dem Set hinzu
// TODO: Gib die finale Länge des Sets aus
}
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