Menu
Coddy logo textTech

Переопределение hashCode и ==

Часть раздела Объектно-ориентированное программирование путешествия по Dart на Coddy — урок 71 из 110.

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

class Point {
  int x, y;
  Point(this.x, this.y);
}

void main() {
  var p1 = Point(3, 4);
  var p2 = Point(3, 4);
  print(p1 == p2);  // false - разные экземпляры!
}

Чтобы сравнивать объекты по их значениям, вам необходимо переопределить как оператор ==, так и hashCode. Эти два метода всегда должны переопределяться вместе — если два объекта равны, они должны иметь одинаковый хэш-код:

class Point {
  final int x, y;
  Point(this.x, this.y);
  
  @override
  bool operator ==(Object other) {
    if (other is! Point) return false;
    return x == other.x && y == other.y;
  }
  
  @override
  int get hashCode => Object.hash(x, y);
}

void main() {
  var p1 = Point(3, 4);
  var p2 = Point(3, 4);
  print(p1 == p2);  // true - одинаковые значения!
}

hashCode имеет решающее значение для таких коллекций, как ключи Set и Map. Вспомогательный метод Dart Object.hash() объединяет несколько значений в один хэш-код. Без правильного hashCode ваши объекты не будут корректно работать в хэш-коллекциях, даже если == переопределен.

challenge icon

Задание

Легко

Давайте создадим систему координат, в которой точки можно сравнивать по их значениям, а не по адресам в памяти! Вы создадите класс Coordinate, который правильно реализует равенство на основе значений, что позволит ему корректно работать с проверками на равенство и коллекциями на основе хешей, такими как Set.

Вы организуете свой код в двух файлах:

  • coordinate.dart: Создайте класс Coordinate, представляющий точку в 2D-пространстве с полями final int x и final int y. Переопределите оператор == и hashCode так, чтобы две координаты с одинаковыми значениями x и y считались равными. Используйте Object.hash() для генерации хеш-кода на основе обоих полей.
  • main.dart: Импортируйте файл с координатами и продемонстрируйте, как равенство на основе значений меняет поведение при сравнении объектов:
    • Создайте два отдельных объекта Coordinate, оба с x: 5 и y: 10
    • Выведите на экран, равны ли они, используя ==
    • Выведите на экран, являются ли они идентичными (один и тот же экземпляр), используя identical()
    • Создайте Set<Coordinate> и добавьте в него обе координаты
    • Выведите длину множества (она должна быть равна 1, так как они равны!)
    • Создайте третью координату с x: 3 и y: 7, добавьте ее в множество
    • Выведите окончательную длину множества

Этот челлендж демонстрирует, почему hashCode и == всегда должны переопределяться вместе. Set использует хеш-коды для организации элементов, поэтому без правильного hashCode даже равные объекты могут храниться как дубликаты!

Ожидаемый вывод:

true
false
1
2

Шпаргалка

По умолчанию два объекта в Dart равны только в том случае, если они являются одним и тем же экземпляром в памяти, а не если у них одинаковые значения:

var p1 = Point(3, 4);
var p2 = Point(3, 4);
print(p1 == p2);  // false - разные экземпляры!

Чтобы сравнивать объекты по их значениям, переопределите оператор == и hashCode. Их всегда нужно переопределять вместе:

class Point {
  final int x, y;
  Point(this.x, this.y);
  
  @override
  bool operator ==(Object other) {
    if (other is! Point) return false;
    return x == other.x && y == other.y;
  }
  
  @override
  int get hashCode => Object.hash(x, y);
}

Используйте Object.hash() для объединения нескольких значений в один хэш-код. hashCode имеет решающее значение для таких коллекций, как Set и Map — без него равные объекты не будут корректно работать в коллекциях на основе хэшей.

Чтобы проверить, являются ли два объекта одним и тем же экземпляром (а не просто имеют равные значения), используйте identical():

print(identical(p1, p2));  // false - разные экземпляры

Попробуйте сами

import 'coordinate.dart';

void main() {
  // TODO: Создайте два отдельных объекта Coordinate, оба с x: 5 и y: 10

  // TODO: Выведите, равны ли они, используя ==

  // TODO: Выведите, идентичны ли они (один и тот же экземпляр), используя identical()

  // TODO: Создайте Set<Coordinate> и добавьте в него обе координаты

  // TODO: Выведите длину множества (должна быть 1, так как они равны!)

  // TODO: Создайте третью координату с x: 3 и y: 7, добавьте ее в множество

  // TODO: Выведите окончательную длину множества
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование