Equality, hashCode, and toString
Prerequisites: Classes and objects
By default, two objects are equal only if they are the same object,
and printing one shows Instance of 'ClassName'. For classes that hold
data, you usually want to change both.
Overriding ==, hashCode, and toString
class Money {
final int cents;
final String currency;
const Money(this.cents, this.currency);
@override
bool operator ==(Object other) =>
other is Money && other.cents == cents && other.currency == currency;
@override
int get hashCode => Object.hash(cents, currency);
@override
String toString() => '${(cents / 100).toStringAsFixed(2)} $currency';
Money operator +(Money other) {
if (other.currency != currency) throw ArgumentError('currency mismatch');
return Money(cents + other.cents, currency);
}
}
void main() {
final a = Money(1050, 'EUR');
final b = Money(1050, 'EUR');
print(a == b);
print({a, b}.length);
print(a);
print(a + Money(250, 'EUR'));
}
Expected output
true
1
10.50 EUR
13.00 EUR
operator ==defines when two objects count as equal.hashCodemust agree with==: equal objects must have equal hash codes, or sets and maps behave incorrectly.Object.hash(...)combines fields correctly.toStringcontrols whatprintand string interpolation show.- Dart also lets you define operators such as
+,<, and[].
Less boilerplate
Writing these by hand for every data class is tedious and easy to get wrong. Common alternatives:
- Records — already compared by value, for simple groupings.
- The
equatablepackage — list the fields once in apropsgetter. - Code generators such as
freezed— generate==,hashCode,toString,copyWith, and JSON methods from a short declaration.
Common mistakes
- Overriding
==withouthashCode. Objects that are "equal" then appear twice in a set. - Making objects with a custom
==mutable. If a field changes while the object is in a set, the set can no longer find it.