Home > database >  Dart equatable with non final variables
Dart equatable with non final variables

Time:05-30

I have class with final variable (hash) and some other non-final variables. Hash is unique value. And objects are stored in Set. Set uses '==' operand to check equality of objects. I want to override "==" and "hashCode" in my class and work with Set array. To avoid using boilerplate code I want to use Equatable extension. Like this

class User extends Equatable {
    final String hash;
    String balance;
    bool state;
    ....

    @override
    List<Object> get props => [hash];
} .... Set<User> users

Is it correct way to use Equatable in my case, 'cause it is uses with immutable classes. Thanks!

CodePudding user response:

Overriding hashCode to depend on non-final fields is usually not recommended because it can make Sets and Maps and other data structures that depend on hashCode internally inconsistent. Suppose you have such an object and insert it into a Set. Later, you mutate that object by assigning a new value to that field, but the Set would still have a reference to that object with the old hash code. For example, consider:

class Foo {
  String s;

  Foo(this.s);

  @override
  bool operator ==(Object other) {
    return other is Foo && s == other.s;
  }

  @override
  int get hashCode => s.hashCode;

  @override
  String toString() => s;
}

void main() {
  var foo = Foo('foo');
  var someSet = <Foo>{foo};
  foo.s = 'bar';
  print(someSet.contains(foo)); // Prints: false
  someSet.add(foo);
  print(someSet.length); // Prints: 2
  print(someSet); // Prints: {bar, bar}
}

and now someSet would have two references to the exact same object, which violates its goal of storing unique objects.

A Map would have similar problems.

Since hashCode is tied to operator ==, this consequently also means that you usually shouldn't override operator == to depend on non-final fields.

You can get away with it if you can guarantee that you never mutate your objects while they're being referenced by a Set/Map/etc. or if you can guarantee that whenever you want to add your object to a Set/Map/etc. that you create a copy of your object and add that copy instead.

CodePudding user response:

I'd say your use is correct.

You use Equatable only with the final field, so the equality and hash code should be stable over time, and the hash field is unique, so it can serve as identifier for the object. If your hash field ends up not unique, you'll have two distinguishable objects that are equal, so ... don't do that.

Not sure how much you gain from using Equatable, though. If you wrote the equality and hash code yourself, it would just be:

  bool operator==(Object other) => other is Foo && hash == other.hash;
  int get hashCode => hash.hashCode;
  • Related