Operator overloading and mixins

We'll take a look at two more Dart features that are useful in some situations but aren't crucial when using Dart, and you'll probably not use them on a daily basis.

Operator overloading

Dart lets us overload its default behavior when using standard operators such as ==, +, -, [], or []=. A typical use case is when using 2D/3D vectors:

class Vector {
  int x, y;
  Vector(this.x, this.y);
  
  operator ==(Vector v) => this.x == v.x && this.y == v.y;
  operator  +(Vector v) => new Vector(this.x + v.x, this.y + v.y);
  operator  -(Vector v) => new Vector(this.x - v.x, this.y - v.y);
}

We can use unit testing to check whether operators work as expected:

var v1 = new Vector(5, 3);
var v2 = new Vector(7, 2);

Vector v3 = v1 + v2;
expect(v3.x, 12);
expect(v3.y, 5);

expect((v1 - v2) == new Vector(-2, 1), isTrue);

var v6 = new Vector(3, 5);
expect(v1 == v6, isFalse);

Unlike JavaScript, there's no === operator (three equal signs) in Dart that compares variables for having the same type and value. The way Dart actually compares objects is up to you by overloading their == operator. To check whether two object reference the same instance, you can use the top-level identity() function.

As another use case, we could extend the book shelf example from previous chapters with overloaded "array subscription" operators, [] and []=:

class Shelf {
  Map<String, Book> books = {};
  
  add(Book book) => books[book.title] = book;
  operator []=(String key, Book book) => books[key] = book;
  operator [](Book book) => books[book.title];
}

class Book {
  String title;
  Book(this.title);
}

This lets us use Shelf objects just like maps, where Book instances are stored with their names as keys:

var shelf = new Shelf();
var b1 = new Book('Book 1'),
var b2 = new Book('Book 2'),

shelf.add(b1);
shelf[b2.title] = b2;
expect(shelf[b1], same(b1));

Well, we could just call shelf.books[b2.title] = b2, but you get the idea how []= operator works.

There are quite a lot of operators to overload and we only used the most common ones here, but take a look at all of them at https://www.dartlang.org/docs/dart-up-and-running/ch02.html#overridable-operators.

Mixins

In Chapter 1, Getting Started with Dart, we mentioned that Dart has a single inheritance model with mixins. The proper definition of mixins is rather complicated (refer to the official definition for Dart at https://www.dartlang.org/articles/mixins/) but simply put, a mixin is a common behavior/feature shared among multiple unrelated classes. In practice, this means a set of methods and properties that are common to all classes implementing this mixin.

We can extend our book shelf example once more by adding a mixin, defining that each book and shelf can be described by keywords that can be arranged into genres:

class Genre {
  List<String> keywords = [];
  
  guessGenre() {
    // Try to guess what's the overall genre for this object.
    // Implementation is not interesting for us here.
  }
}
class Shelf extends Object with Genre {
  /* Remains unchanged. */
}

class Book extends Object with Genre {
  /* Remains unchanged. */
}

Mixins are implemented by the with keyword. Each class implementing mixins has to specify its parent class even when it's the default Object class. Both Shelf and Book classes now have the keywords property and a guessGenre() method.

The Genre mixin is just a class that has to follow three requirements:

  • No constructors defined
  • The super class is the default Object class
  • No calls to the parent class with super

We can test both classes whether they properly implement the Genre mixin. Note that one class can implement multiple mixins at the same time:

// Objects created as above.
expect(shelf, new isInstanceOf<Genre>());
expect(b1, new isInstanceOf<Genre>());

expect(shelf.keywords, new isInstanceOf<List>());
expect(b1.keywords, new isInstanceOf<List>());

expect(shelf.guessGenre, new isInstanceOf<Function>());
expect(b1.guessGenre, new isInstanceOf<Function>());

When any of these tests fail, the code throws an exception, so outside unit tests, we could check whether an object implements a mixin rather with the is keyword:

if (shelf is Genre) {
  // Object shelf of class Shelf implements Genre mixin.
}
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.137.172.115