Operator Overloading, Equality and Callable Objects

22.1 Operator Overloading

Overloadable: < > <= >= - + / ~/ * % | ^ & << >> >>> [] []= ~ ==. There are no custom operators.

class Vector {
  final double x, y;
  const Vector(this.x, this.y);

  Vector operator +(Vector other) => Vector(x + other.x, y + other.y);
  Vector operator -(Vector other) => Vector(x - other.x, y - other.y);
  Vector operator *(double k) => Vector(x * k, y * k);
  Vector operator -() => Vector(-x, -y);
  double operator [](int i) => i == 0 ? x : y;
  bool operator <(Vector o) => length < o.length;

  double get length => (x * x + y * y);   // squared length for brevity

  @override
  bool operator ==(Object other) => other is Vector && other.x == x && other.y == y;
  @override
  int get hashCode => Object.hash(x, y);
  @override
  String toString() => 'Vector($x, $y)';
}

void main() {
  final v = const Vector(1, 2) + const Vector(3, 4) * 2;
  print((v, -v, v[0], v == const Vector(7, 10)));
}

22.2 Equality and hashCode

Rule: if you override ==, override hashCode (equal objects must have equal hash codes). Use Object.hash, Object.hashAll, and Object.hashAllUnordered.

Avoid the boilerplate with the equatable package, the freezed code generator, or records.

class Person {
  final String name;
  final int age;
  const Person(this.name, this.age);

  @override
  bool operator ==(Object other) =>
      identical(this, other) || other is Person && other.name == name && other.age == age;

  @override
  int get hashCode => Object.hash(name, age);
}

22.3 Callable Objects and Comparable

class Multiplier {
  final int factor;
  Multiplier(this.factor);
  int call(int value) => value * factor;   // makes instances callable
}
final triple = Multiplier(3);
print(triple(5));                          // 15

class Version implements Comparable<Version> {
  final int major, minor;
  Version(this.major, this.minor);
  @override
  int compareTo(Version other) =>
      major != other.major ? major.compareTo(other.major) : minor.compareTo(other.minor);
}
final sorted = [Version(1, 10), Version(1, 2)]..sort();

22.4 noSuchMethod

class Mock {
  @override
  dynamic noSuchMethod(Invocation invocation) {
    print('Called ${invocation.memberName} with ${invocation.positionalArguments}');
    return null;
  }
}
(Mock() as dynamic).anything(1, 2);   // only for dynamic calls or classes that implement interfaces without bodies