Pull Request
#213
Summary
Change the default toString() implementation in equatable.dart and equatable_mixin.dart to delegate to super.toString() instead of returning '$runtimeType' directly. For EquatableMixin, this allows classes that mix it in to correctly inherit toString() behavior from their superclass hierarchy. For Equatable, this is a consistency alignment with standard Dart behavior.
Motivation
Currently, the default toString() implementation in both Equatable and EquatableMixin is:
@override
String toString() => '$runtimeType';
EquatableMixin
Because EquatableMixin is a mixin, it can be applied to a class that already extends another class with a meaningful toString(). In this scenario, the mixin silently shadows that superclass implementation with a bare runtime type string without any visible indication in the consuming code.
This is a source of subtle, hard-to-detect bugs. The behavior changes as soon as EquatableMixin is added to the class declaration, and there is no way for the developer to notice unless they explicitly verify the output. Changing the implementation to return super.toString() restores the expected Dart method resolution order (MRO), allowing superclass toString() implementations to propagate correctly through the inheritance chain.
Equatable
Since Equatable is an abstract class that does not extend any class other than Object, changing to super.toString() does not unlock any superclass inheritance benefit. The call simply resolves to Object.toString(), which returns Instance of 'ClassName' instead of the current '$runtimeType'. The motivation here is purely consistency aligning the behavior with standard Dart conventions rather than introducing a custom format.
Proposed Change
- @override
- String toString() => '$runtimeType';
+ @override
+ String toString() => super.toString();
This change applies to both:
equatable.dart
equatable_mixin.dart
Behavior Comparison (EquatableMixin)
Before
import 'package:equatable/equatable.dart';
abstract class Animal {
const Animal();
@override
String toString() => 'THIS IS A ANIMAL';
}
class Dog extends Animal with EquatableMixin {
const Dog({required this.name});
final String name;
@override
List<Object?> get props => [name];
}
void main() {
print(const Dog(name: 'Spot')); // 'Dog' ← superclass toString() is silently ignored
}
After
import 'package:equatable/equatable.dart';
abstract class Animal {
const Animal();
@override
String toString() => 'THIS IS A ANIMAL';
}
class Dog extends Animal with EquatableMixin {
const Dog({required this.name});
final String name;
@override
List<Object?> get props => [name];
}
void main() {
print(const Dog(name: 'Spot')); // 'THIS IS A ANIMAL' ← superclass toString() is now respected
}
Note that Dog itself is unchanged the output changes solely because EquatableMixin no longer silently swallows the superclass implementation.
Impact on Existing Codebases
This is a behavioral breaking change for any class that:
- Mixes in
EquatableMixin, and
- Does not override
toString() itself, and
- Inherits a non-default
toString() from a superclass.
Additionally, any class extending Equatable or mixing in EquatableMixin that relies on the current '$runtimeType' string format will observe a change to Instance of 'ClassName'. Teams are advised to audit usages of .toString() (including implicit calls via string interpolation or print) on classes that extend or mix in equatable abstractions.
Notes
- Classes that already define their own
toString() override are most likely unaffected.
- The superclass propagation benefit described above applies exclusively to
EquatableMixin, as it is the only one that can be combined with an independent class hierarchy.
- This change aligns
equatable with the principle of least surprise and the expected Dart method resolution order.
Pull Request
#213
Summary
Change the default
toString()implementation inequatable.dartandequatable_mixin.dartto delegate tosuper.toString()instead of returning'$runtimeType'directly. ForEquatableMixin, this allows classes that mix it in to correctly inherittoString()behavior from their superclass hierarchy. ForEquatable, this is a consistency alignment with standard Dart behavior.Motivation
Currently, the default
toString()implementation in bothEquatableandEquatableMixinis:EquatableMixinBecause
EquatableMixinis a mixin, it can be applied to a class that already extends another class with a meaningfultoString(). In this scenario, the mixin silently shadows that superclass implementation with a bare runtime type string without any visible indication in the consuming code.This is a source of subtle, hard-to-detect bugs. The behavior changes as soon as
EquatableMixinis added to the class declaration, and there is no way for the developer to notice unless they explicitly verify the output. Changing the implementation toreturn super.toString()restores the expected Dart method resolution order (MRO), allowing superclasstoString()implementations to propagate correctly through the inheritance chain.EquatableSince
Equatableis an abstract class that does not extend any class other thanObject, changing tosuper.toString()does not unlock any superclass inheritance benefit. The call simply resolves toObject.toString(), which returnsInstance of 'ClassName'instead of the current'$runtimeType'. The motivation here is purely consistency aligning the behavior with standard Dart conventions rather than introducing a custom format.Proposed Change
This change applies to both:
equatable.dartequatable_mixin.dartBehavior Comparison (
EquatableMixin)Before
After
Note that
Dogitself is unchanged the output changes solely becauseEquatableMixinno longer silently swallows the superclass implementation.Impact on Existing Codebases
This is a behavioral breaking change for any class that:
EquatableMixin, andtoString()itself, andtoString()from a superclass.Additionally, any class extending
Equatableor mixing inEquatableMixinthat relies on the current'$runtimeType'string format will observe a change toInstance of 'ClassName'. Teams are advised to audit usages of.toString()(including implicit calls via string interpolation orprint) on classes that extend or mix in equatable abstractions.Notes
toString()override are most likely unaffected.EquatableMixin, as it is the only one that can be combined with an independent class hierarchy.equatablewith the principle of least surprise and the expected Dart method resolution order.