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 Set
s and Map
s 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;