You are currently viewing Dart: Operator Overloading

Dart: Operator Overloading

Imagine you have two toy boxes. If you put them together, you have one big toy box. In math, we use the + operator to add numbers together, like this:

int sum = 2 + 3; // sum is 5

But what if we wanted to use + on our ToyBox objects?

Wouldn’t it be cool if Dart understood that adding two toy boxes means combining their toys? That’s exactly what operator overloading allows us to do!

In this article, we’ll learn how to teach Dart’s operators new tricks so they can work with our own objects.

Understanding Operators in Dart

Operators are special symbols like +, -, *, /, and == that let us do math and comparisons. Normally, these work on numbers:

int a = 5 + 3; // 8
int b = 10 - 4; // 6

Dart knows that + means add and - means subtract, but it doesn’t know what to do if we try this with our own objects:

ToyBox box1 = ToyBox(5);  
ToyBox box2 = ToyBox(3);  
ToyBox box3 = box1 + box2; // Error! Dart doesn't know how to add ToyBoxes.

To fix this, we need to teach Dart what + should do when used with ToyBox objects.

How to Overload Operators in Dart

To overload an operator in Dart, we use the operator keyword inside a class. Let’s create a ToyBox class where + will add the number of toys from two boxes.

class ToyBox {

  int toys;

  ToyBox(this.toys);

  // Overloading the + operator
  ToyBox operator +(ToyBox other) {
    return ToyBox(this.toys + other.toys);
  }

}

void main() {

  ToyBox box1 = ToyBox(5);
  ToyBox box2 = ToyBox(3);

  ToyBox bigBox = box1 + box2; // Now Dart knows how to add ToyBoxes!

  print("Total toys: ${bigBox.toys}"); // Output: Total toys: 8

}

How Does This Work?

  • The operator + method tells Dart what to do when + is used with two ToyBox objects.
  • It adds the number of toys in both boxes and returns a new ToyBox.
  • Now, box1 + box2 works just like adding numbers!

Practical Examples of Operator Overloading

Overloading + for a Point Class

Let’s say we have a Point class that represents coordinates (x, y) on a grid. We can overload + to add two points together:

class Point {

  int x, y;

  Point(this.x, this.y);

  Point operator +(Point other) {
    return Point(this.x + other.x, this.y + other.y);
  }

}

void main() {

  Point p1 = Point(2, 3);
  Point p2 = Point(4, 5);

  Point p3 = p1 + p2; // Adds x and y values

  print("New Point: (${p3.x}, ${p3.y})"); // Output: New Point: (6, 8)

}

Now we can add points together just like numbers!

Overloading == to Compare Objects

In Dart, when we compare two objects using the == operator, it checks if they are the same object in memory, not if they have the same values. This means that even if two objects look identical, Dart might still say they are different if they are stored in different places in memory.

However, in some cases, you might want to compare the values of objects, not just their memory addresses. For example, if you have two Point objects representing the same coordinates, you’d want Dart to consider them equal.

We can overload == to check if two objects have the same values. But there’s an important rule to follow when doing this: whenever we override the == operator, we must also override the hashCode. This ensures that objects that are considered equal also have the same hash code, which is important for using objects in collections like sets and maps.

Here’s an example that demonstrates how to overload == and hashCode for a Point class:

class Point {

  int x, y;

  Point(this.x, this.y);

  // Overloading the == operator to compare two Points
  @override
  bool operator ==(Object other) {

    if (other is Point) {
      return this.x == other.x && this.y == other.y;
    }

    return false;

  }

  // Overriding hashCode to return a consistent hash code for equal objects
  @override
  int get hashCode {
    return x.hashCode ^ y.hashCode; // Combining the hash codes of x and y
  }

}

void main() {

  Point p1 = Point(2, 3);
  Point p2 = Point(2, 3);
  Point p3 = Point(4, 5);

  print(p1 == p2); // true (because both points have the same x and y values)
  print(p1 == p3); // false (because the points are different)

  // Using Points in a set (which relies on hashCode and ==)
  Set<Point> points = {p1, p2, p3};
  print(points); // Should only have two distinct Points (p1 and p3)

}

Why Override hashCode?

In Dart, collections like sets and maps use the hashCode to quickly check if objects are equal. If two objects are considered equal by the == operator, they must return the same hash code. This is especially important when using objects in a set or map, as these collections rely on hashCode for efficient searching and storing.

In the above example, when we override the == operator to compare two Point objects based on their x and y values, we also override the hashCode method. We combine the hash codes of x and y using the bitwise XOR (^) operator to create a unique and consistent hash code for each Point.

Key Takeaways:

  • When you override == to compare objects, always remember to also override hashCode to maintain consistency.
  • Objects that are considered equal using == must have the same hashCode.
  • Failing to override hashCode properly can lead to unexpected behavior in collections like sets and maps.

By following these rules, you ensure that Dart can properly compare objects based on their values and efficiently use them in collections.

Overloading * to Scale an Object

What if we want to multiply a Point by a number to make it bigger?

class Point {
  int x, y;

  Point(this.x, this.y);

  Point operator *(int factor) {
    return Point(this.x * factor, this.y * factor);
  }

}

void main() {

  Point p1 = Point(2, 3);

  Point p2 = p1 * 2; // Makes x and y twice as big

  print("Scaled Point: (${p2.x}, ${p2.y})"); // Output: Scaled Point: (4, 6)

}

Now multiplying a Point by a number makes it bigger!

When to Use Operator Overloading

Operator overloading is useful when:

  • You want to improve code readability (e.g., adding objects directly rather than using methods).
  • You work with math-related objects (e.g., points, vectors, and matrices).
  • You need to compare objects in a meaningful way.

Conclusion

You now know how to teach Dart’s operators new tricks! You’ve learned how to:

  • Overload the + operator to add objects.
  • Overload == to compare objects correctly.
  • Overload * to scale objects.

Operator overloading makes your code cleaner, more powerful, and enjoyable to write! Try using it in your Dart programs and explore the creative possibilities!

Leave a Reply