blob: a2c10bcfed10d4247a8a35b947ddc31992b827af [file] [log] [blame] [view]
Felix Angelov6f2d66d2019-01-07 13:53:35 -06001<img src="https://github.com/felangel/equatable/raw/master/doc/assets/equatable_logo_full.png" width="100%" alt="logo" />
Felix Angelovd778e3c2019-01-06 22:07:28 -06002<h2 align="center">
Felix Angelovb3762832019-01-06 22:04:01 -06003 Simplify Equality Comparisons
Felix Angelovd778e3c2019-01-06 22:07:28 -06004</h2>
Felix Angelovda3586e2019-01-06 20:49:23 -06005<p align="center">
6 <a href="https://travis-ci.org/felangel/equatable">
7 <img alt="Build Status" src="https://travis-ci.org/felangel/equatable.svg?branch=master">
8 </a>
9 <a href="https://codecov.io/gh/felangel/equatable">
10 <img alt="Code Coverage" src="https://codecov.io/gh/felangel/equatable/branch/master/graph/badge.svg">
11 </a>
12 <a href="https://pub.dartlang.org/packages/equatable">
13 <img alt="Pub Package" src="https://img.shields.io/pub/v/equatable.svg">
14 </a>
15 <br/>
16 <a href="https://opensource.org/licenses/MIT">
17 <img alt="MIT License" src="https://img.shields.io/badge/License-MIT-blue.svg">
18 </a>
19 <a href="https://gitter.im/equatable_package/community">
20 <img alt="Gitter" src="https://img.shields.io/badge/gitter-equatable-yellow.svg">
21 </a>
22</p>
Felix Angelov774a32e2019-01-06 13:20:35 -060023
Felix Angeloveac630f2019-01-06 20:32:28 -060024---
25
Felix Angelov774a32e2019-01-06 13:20:35 -060026## Overview
27
28Being able to compare objects in `Dart` often involves having to override the `==` operator as well as `hashCode`.
29
30Not only is it verbose and tedious, but failure to do so can lead to inefficient code which does not behave as we expect.
31
32By default, `==` returns true if two objects are the same instance.
33
34Let's say we have the following class:
35
36```dart
37class Person {
Felix Angelovda3586e2019-01-06 20:49:23 -060038 final String name;
Felix Angelov774a32e2019-01-06 13:20:35 -060039
Felix Angelovda3586e2019-01-06 20:49:23 -060040 const Person(this.name);
Felix Angelov774a32e2019-01-06 13:20:35 -060041}
42```
43
44We can create create instances of `Person` like so:
45
46```dart
47void main() {
Felix Angelovda3586e2019-01-06 20:49:23 -060048 final Person bob = Person("Bob");
Felix Angelov774a32e2019-01-06 13:20:35 -060049}
50```
51
52Later if we try to compare two instances of `Person` either in our production code or in our tests we will run into a problem.
53
54```dart
55print(bob == Person("Bob")); // false
56```
57
58For more information about this, you can check out the official [Dart Documentation](https://www.dartlang.org/guides/language/effective-dart/design#equality).
59
60In order to be able to compare two instances of `Person` we need to change our class to override `==` and `hashCode` like so:
61
62```dart
63class Person {
Felix Angelovda3586e2019-01-06 20:49:23 -060064 final String name;
Felix Angelov774a32e2019-01-06 13:20:35 -060065
Felix Angelovda3586e2019-01-06 20:49:23 -060066 const Person(this.name);
Felix Angelov774a32e2019-01-06 13:20:35 -060067
Felix Angelovda3586e2019-01-06 20:49:23 -060068 @override
69 bool operator ==(Object other) =>
70 identical(this, other) ||
71 other is Person &&
72 runtimeType == other.runtimeType &&
73 name == other.name;
Felix Angelov774a32e2019-01-06 13:20:35 -060074
Felix Angelovda3586e2019-01-06 20:49:23 -060075 @override
76 int get hashCode => name.hashCode;
Felix Angelov774a32e2019-01-06 13:20:35 -060077}
78```
79
80Now if we run the following code again:
81
82```dart
83print(bob == Person("Bob")); // true
84```
85
86it will be able to compare different instances of `Person`.
87
88You can see how this can quickly become a hassle when dealing with complex classes. This is where `Equatable` comes in!
89
Felix Angelovdc2523e2019-01-06 13:31:51 -060090## What does Equatable do?
Felix Angelov774a32e2019-01-06 13:20:35 -060091
92`Equatable` overrides `==` and `hashCode` for you so you don't have to waste your time writing lots of boilerplate code.
93
94There are other packages that will actually generate the boilerplate for you; however, you still have to run the code generation step which is not ideal.
95
96With `Equatable` there is no code generation needed and we can focus more on writing amazing applications and less on mundane tasks.
97
98## Usage
99
100First, we need to do add `equatable` to the dependencies of the `pubspec.yaml`
101
102```yaml
103dependencies:
Felix Angelovf8c24002019-01-24 17:32:25 -0600104 equatable: ^0.1.0
Felix Angelov774a32e2019-01-06 13:20:35 -0600105```
106
107Next, we need to install it:
108
109```sh
110# Dart
111pub get
112
113# Flutter
114flutter packages get
115```
116
117Lastly, we need to extend `Equatable`
118
119```dart
120import 'package:equatable/equatable.dart';
121
122class Person extends Equatable {
Felix Angelovda3586e2019-01-06 20:49:23 -0600123 final String name;
Felix Angelov774a32e2019-01-06 13:20:35 -0600124
Felix Angelovda3586e2019-01-06 20:49:23 -0600125 Person(this.name) : super([name]);
Felix Angelov774a32e2019-01-06 13:20:35 -0600126}
127```
128
129We can now compare instances of `Person` just like before without the pain of having to write all of that boilerplate.
Felix Angelovda3586e2019-01-06 20:49:23 -0600130
131## Recap
132
133### Without Equatable
134
135```dart
136class Person {
137 final String name;
138
139 const Person(this.name);
140
141 @override
142 bool operator ==(Object other) =>
143 identical(this, other) ||
144 other is Person &&
145 runtimeType == other.runtimeType &&
146 name == other.name;
147
148 @override
149 int get hashCode => name.hashCode;
150}
151```
152
153### With Equatable
154
155```dart
156import 'package:equatable/equatable.dart';
157
158class Person extends Equatable {
159 final String name;
160
161 Person(this.name) : super([name]);
162}
163```
Felix Angelov92a5eb62019-01-09 22:44:58 -0600164
Felix Angelov8a5ca342019-02-20 20:18:34 -0600165## EquatableMixin
166
167Sometimes it isn't possible to extend `Equatable` because your class already has a superclass.
168In this case, you can still get the benefits of `Equatable` by using the `EquatableMixin`.
169
170### Usage
171
172Let's say we want to make an `EquatableDateTime` class, we can use `EquatableMixinBase` and `EquatableMixin` like so:
173
174```dart
175class EquatableDateTime extends DateTime
176 with EquatableMixinBase, EquatableMixin {
177 EquatableDateTime(
178 int year, [
179 int month = 1,
180 int day = 1,
181 int hour = 0,
182 int minute = 0,
183 int second = 0,
184 int millisecond = 0,
185 int microsecond = 0,
186 ]) : super(year, month, day, hour, minute, second, millisecond, microsecond);
187
188 @override
189 List get props {
190 return [year, month, day, hour, minute, second, millisecond, microsecond];
191 }
192}
193```
194
195Now if we want to create a subclass of `EquatableDateTime`, we can continue to just use the `EquatableMixin` and override `props`.
196
197```dart
198class EquatableDateTimeSubclass extends EquatableDateTime with EquatableMixin {
199 final int century;
200
201 EquatableDateTime(
202 this.century,
203 int year,[
204 int month = 1,
205 int day = 1,
206 int hour = 0,
207 int minute = 0,
208 int second = 0,
209 int millisecond = 0,
210 int microsecond = 0,
211 ]) : super(year, month, day, hour, minute, second, millisecond, microsecond);
212
213 @override
214 List get props => super.props..addAll([century]);
215}
216```
217
Felix Angelov92a5eb62019-01-09 22:44:58 -0600218## Performance
219
220You might be wondering what the performance impact will be if you use `Equatable`.
221
222[Performance Tests](https://github.com/felangel/equatable/raw/master/performance_tests) have been written to test how `Equatable` stacks up to manually overriding `==` and `hashCode` in terms of class instantiation as well as equality comparison.
223
224
225### Results (average over 10 runs)
226
227#### Equality Comparison A == A
228
229| Class | Runtime (microseconds) |
230| ------------------ | ---------------------- |
231| RAW | 0.143 |
232| Empty Equatable | 0.124 |
233| Hydrated Equatable | 0.126 |
234
235#### Instantiation A()
236
237| Class | Runtime (microseconds) |
238| ------------------ | ---------------------- |
239| RAW | 0.099 |
240| Empty Equatable | 0.121 |
241| Hydrated Equatable | 0.251 |