Как современный язык программирования, Dart предлагает разработчикам множество тонкостей и синтаксического сахара. В последнее время я довольно часто начал использовать перегрузку операторов, чтобы добавить легко читаемую функциональность классам в моей модели.

Перегрузка оператора

Для тех, кто не слышал об этом, позвольте мне объяснить, что это на самом деле.
Перегрузка операторов дает вам возможность добавлять собственные реализации к определенному набору операторов в ваших классах. Эти операторы могут быть:

  • Арифметические операторы, такие как + - * и т. Д.
  • Операторы равенства, такие как == и !=
  • Операторы отношения, > >= < и <=
  • и многое другое

Эту языковую функцию также можно найти в других языках, таких как Kotlin, Groovy, C ++ и т. Д.

Например, у нас есть приложение с двумя классами Person и Family. Перегрузив оператор плюса, мы получим следующее:

Family family = new Person() + new Person();

Для этого у нас должны быть следующие классы:

class Person {
  Family operator +(Person other) {
    return Family([this, other]);
  }
}
class Family {
  final List<Person> people;
  Family(this.people);
}

Давайте подробнее рассмотрим код, который перегружает оператор.

Family operator +(Person other) {
  return Family([this, other]);
}

Мы используем зарезервированное ключевое слово operator и конкретный оператор, который мы хотим перегрузить, в данном случае оператор плюс: +
В качестве возвращаемого типа мы определяем Family. В теле функции мы просто создаем объект Family, передавая литерал List с обоими объектами Person.

Порядок операндов идет слева направо, что означает, что this - это левый Person, а other - правый.

Другой пример

Может быть, вы думаете, это полезно? Что ж, семейный пример на самом деле не лучший вариант использования, но я смог использовать его с пользой, когда делал приложение Flutter, чтобы отслеживать потребление коммунальных услуг в доме.

class MeterReading {
  final double electricity;
  final double gas;
  final double water;
  MeterReading({this.electricity, this.gas, this.water});
  Consumption operator -(MeterReading other) => Consumption(
      electricity: (electricity - other.electricity).abs(),
      gas: (gas - other.gas).abs(),
      water: (water - other.water).abs()
    );
}

Перегрузив оператор минус, я смог использовать этот синтаксический сахар в своих функциях вычисления:

Consumption consumption = reading - previousReading;

Еще мне нужно было использовать множитель. Если бы потребление было измерено между 10 марта и 10 апреля, мне пришлось бы умножить потребление примерно на 0,67, чтобы получить частичное потребление за март. Впоследствии я мог бы добавить это к потреблению за первые 10 дней марта, перегрузив оператор плюс.

// Consumption.dart
Consumption operator *(double multiplier) => Consumption(
      electricity: electricity * multiplier,
      gas: gas * multiplier,
      water: water * multiplier,
      );
Consumption operator +(Consumption other) => Consumption(
    electricity: electricity + other?.electricity,
    gas: gas + other?.gas,
    water: water + other?.water,
  );

Порядок операторов

Во время разработки этого инструмента я неожиданно получил неожиданные результаты.

Consumption one = Consumption(electricity: 15, gas: 3, water: 9);
Consumption two = Consumption(electricity: 5, gas: 7, water: 11);
Consumption result = one + two * 0.5;
print("Result is $result");

Я ожидал получить следующий результат:
›Результат - электричество: 10,0, газ: 5,0, вода: 10,0
Вместо этого я получил:
› Результат: электричество: 17,5, газ: 6,5, вода: 14,5.

Причина этого в том, что эти операторы по-прежнему имеют определенный приоритет операций. Это заставило приведенный выше код сначала выполнить two * 0.5, а затем выполнить операцию плюса. Добавление круглых скобок устранило эту проблему.

Consumption result = (one + two) * 0.5

Больше операторов

Помимо тех, что я показал, в Dart есть намного больше операторов, которые можно переопределить для пользовательских реализаций.

Следует упомянуть два интересных аспекта: оператор доступа к списку [] и оператор вызова функции () путем реализации call(). Это может помочь добавить больше синтаксического сахара в вашу модель или даже создать предметно-ориентированный язык в Dart.

Узнайте больше о перегрузке операторов в Dart в документации и дайте мне знать, как вы их используете! :-)