DMarlon Studio

Lesson 12 · 12 of 20

Operators, == and hashCode · Dart

Define operators on your Dart classes, compare objects by value with == and hashCode, and sort them with Comparable, using a Money type.

Dart · Operators, == and hashCode in DMarlon Studio: the example's code and its output in the terminal

In Dart, operators are methods with a special name: `operator +`, operator *, operator <… Money stores cents and a currency, and defines + to add amounts (throwing an ArgumentError when the currencies differ), * to multiply by a quantity and < to compare. That way, coffee * 2 + croissant reads like a café bill.

By default, == compares identity: two objects are equal only if they are the same object. To make two Money(150) equal you override `operator ==`, which takes an Object, checks the type with is Money and compares the fields. identical still says false: they are equal, but not the same object.

Golden rule: if you override ==, override `hashCode` too, so that equal objects produce the same number. Object.hash(cents, currency) computes it from the same fields == compares. Sets and maps rely on it to look things up, which is why the set {coffee, Money(150), croissant} ends up with only two prices.

Implementing `Comparable<Money>` with compareTo gives the class a natural order, and sort() uses it with no extra arguments. toString() decides how the object is printed, inside lists and sets too. The last case shows the error when adding euros to dollars.

Code · main.dart

class Money implements Comparable<Money> {  final int cents;  final String currency;  const Money(this.cents, [this.currency = 'EUR']);  Money operator +(Money other) {    if (other.currency != currency) throw ArgumentError('$currency + ${other.currency}');    return Money(cents + other.cents, currency);  }  Money operator *(int times) => Money(cents * times, currency);  bool operator <(Money other) => cents < other.cents;  @override  int compareTo(Money other) => cents.compareTo(other.cents); // for sort()  // Equal by value: same amount and currency. hashCode must agree with ==.  @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';}void main() {  final coffee = Money(150), croissant = Money(195);  print('Order: ${coffee * 2 + croissant}, cheaper: ${coffee < croissant}');  print('== ${Money(150) == coffee}, identical ${identical(Money(150), coffee)}');  print('Distinct prices: ${{coffee, Money(150), croissant}}'); // a Set  print('Sorted: ${[Money(999), coffee, croissant]..sort()}');  try { coffee + Money(150, 'USD'); } on ArgumentError catch (e) { print('Cannot add ${e.message}'); }}

Output · dart run main.dart

Order: 4.95 EUR, cheaper: true
== true, identical false
Distinct prices: {1.50 EUR, 1.95 EUR}
Sorted: [1.50 EUR, 1.95 EUR, 9.99 EUR]
Cannot add EUR + USD

Key points

  • operator + and friends define operators on your classes.
  • Override == to compare by value.
  • == always goes with hashCode, computed from the same fields.
  • Object.hash combines several fields into one hash code.
  • Comparable and compareTo let sort() order your objects.

Exercise

Add operator - to Money and work out the change a customer gets when paying the order with a 5-euro note (Money(500)).