dart(flutter)のオブジェクトの等価性を独自に定義する

オブジェクト同士を比較する

以下のようなクラスがあるとします。

class Person {
  final int id;
  final String name;
  const Person({ required this.id, required this.name });

  Person copyWith({ int? id, String? name }) {
    return Person(id: id ?? this.id, name: name ?? this.name);
  }
}

オブジェクト同士の==による比較は、フィールドの値が同じでも、新しく生成されたオブジェクトの場合参照先が異なるためfalseになります。

// 以下の判定は、いずれも false になります。

final p1 = Person(id: 1, name: '太郎');
final p2 = Person(id: 1, name: '太郎');

p1 == p2; // false

[p1].contains(p2); // false

しかし、人間なら年齢や職業などの状態が変化しても別人に変わらないように、idが同じなら、オブジェクトの参照先や他のフィールドの値が異なっても同一と判定できた方が処理をシンプルに記述できる場合もあると思います。

final list = [
  Person(id: 1, name: '太郎'),
  Person(id: 2, name: '次郎'),
];

final person = Person(id: 1, name: '太郎2');


// リスト内にpersonが含まれるかチェック
if (list.contains(person)) {
  // do something
}

このような場合は、id同士を比較することなく、オブジェクトを直接比較できたら楽です。

オブジェクトの等価性を独自に定義する

==演算子を override します

class Person {
  final int id;
  final String name;
  const Person({ required this.id, required this.name });

  Person copyWith({ int? id, String? name }) {
    return Person(id: id ?? this.id, name: name ?? this.name);
  }

  @override
  bool operator ==(other) {
    if (identical(this, other)) {
      return true;
    }
    return other is Person && id == other.id;
  }
  
  @override
  int get hashCode => id.hashCode;
}

これで、オブジェクト同士を==で比較した際に、idが同じなら、trueと判定されるようになりました。

final p1 = Person(id: 1, name: '太郎1');
final p2 = Person(id: 1, name: '太郎2');

p1 == p2; // id が同じなので、true

[p1].contains(p2); // id が同じなので、true