Что наследуется от класса Object в Dart?

«Что наследуется от класса Object в Dart?» — вопрос из категории Dart Core, который задают на 29% собеседований Flutter Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

В Dart все классы неявно наследуются от встроенного класса Object. Это означает, что любой экземпляр любого класса имеет набор базовых методов, определенных в Object.

Основные унаследованные члены:

  1. String toString() — возвращает строковое представление объекта. По умолчанию выводит что-то вроде Instance of 'Person'. Я часто переопределяю его для отладки.
  2. int get hashCode — возвращает целочисленный хэш-код объекта, используемый в коллекциях вроде HashMap и HashSet.
  3. bool operator ==(Object other) — оператор равенства. Важнейший момент: по умолчанию он сравнивает ссылки (эквивалентно identical(this, other)), а не содержимое объектов.
  4. Type get runtimeType — возвращает тип объекта во время выполнения (runtime).

Критическая связь между == и hashCode: Если вы переопределяете operator ==, вы обязаны также переопределить hashCode. Объекты, которые равны по ==, должны возвращать одинаковый хэш-код. Нарушение этого контракта приведет к некорректной работе хэш-коллекций.

Пример переопределения в классе модели данных Flutter:

class User {
  final String id;
  final String name;

  const User({required this.id, required this.name});

  @override
  bool operator ==(Object other) {
    // Проверка идентичности ссылок (оптимизация)
    if (identical(this, other)) return true;
    // Проверка типа и сравнение значимых полей
    return other is User &&
        runtimeType == other.runtimeType &&
        id == other.id; // Сравниваем только id, так как он уникален
  }

  @override
  int get hashCode => id.hashCode; // Хэш-код должен зависеть от тех же полей, что и ==

  @override
  String toString() => 'User(id: $id, name: $name)'; // Удобно для логов
}

void main() {
  final user1 = User(id: '1', name: 'Alice');
  final user2 = User(id: '1', name: 'Alice');
  final user3 = User(id: '2', name: 'Alice');

  print(user1 == user2); // true (id одинаковые)
  print(user1 == user3); // false (id разные)

  // Благодаря правильному hashCode, объекты корректно работают в Set
  final usersSet = {user1, user2, user3};
  print(usersSet.length); // 2 (user1 и user3, user2 не добавится, так как равен user1)
}

Понимание этого механизма фундаментально для корректной работы с коллекциями, такими как Set и ключами Map, а также для написания тестов с expect().