-
Notifications
You must be signed in to change notification settings - Fork 1
/
generics.dart
302 lines (253 loc) · 6.49 KB
/
generics.dart
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
// Generics
void main(List<String> args) {
// Part 01 - Generic Integer or Double
final double doubleValue = eitherIntOrDouble();
print(doubleValue);
final int intValue = eitherIntOrDouble();
print(intValue);
// Part 02 - Type Matching
print(doTypesMatch(1, 2));
print(doTypesMatch(1, 2.2));
// Part 03 - Constrained Generic Type Definitions
const momAndDad = {
'mom': Person(),
'dad': Person(),
};
const brotherAndSisterAndMyFish = {
'Brother': Person(),
'Sister': Person(),
'Fishy': Fish(),
};
final allValues = [
momAndDad,
brotherAndSisterAndMyFish,
];
describe(allValues);
// Part 04 - Unconstrained Generic Type Definitions
const one = KeyValue(1, 2);
print(one);
const two = KeyValue(1, 2.2); // MapEntry<int, double>
print(two);
// Part 05 - Specializing Generic Type Definitions
const JSON<String> json = {
'name': 'John',
'address': '123 Main St',
};
print(json);
// Part 06 - Generic Mixins and Specialized Mixin Type Definitions
const person = Person2(height: 1.7);
const dog = Dog2(height: 1);
print(person.height);
print(dog.height);
// Part 07 - Generic Classes with Generic Extensions
const tuple = Tuple(1, 20.2);
print(tuple);
final swapped = tuple.swap();
print(swapped);
// Part 08 - Generic Sorting with Comparable
sort(ascending: false);
sort(ascending: true);
// Part 09 - Handling Lack of Common Ancestors
print((2.2).toInt() == 2);
print((2.0).toInt() == 2);
print('3'.toInt() == 3);
print(['2', '3.5'].toInt() == 6);
print({'2', 3, '4.2'}.toInt() == 9);
print(['2', 3, '4.2', 5.3].toInt() == 14);
// Part 10 - Generic Extension on Any Data Type
final personName = personThing.mapIfOfType(
(Person3 p) => p.name,
) ??
'Unknown person name';
print(personName);
final animalName = animalThing.mapIfOfType(
(Animal a) => a.name,
) ??
'Unknown animal name';
print(animalName);
// Part 11 - Generic Function Pointers
const integers = [100, 5, 2];
const doubles = [33.0, 3, 2.0];
print(integers.reduce(divide));
print(doubles.reduce(divide));
// Part 12 - Generic Class Properties
print(Person4(age: 10.2).ageRounded);
}
// Part 01 - Generic Integer or Double
T eitherIntOrDouble<T extends num>() {
switch (T) {
case int:
return 1 as T;
case double:
return 1.1 as T;
default:
throw Exception('Not supported');
}
}
// Part 01 - END
// Part 02 - Type Matching
// bool doTypesMatch(Object a, Object b) {
// return a.runtimeType == b.runtimeType;
// }
bool doTypesMatch<L, R>(L a, R b) => L == R;
// Part 02 - END
// Part 03 - Constrained Generic Type Definitions
typedef BreathingThings<T extends CanBreathe> = Map<String, T>;
void describe(Iterable<BreathingThings> values) {
for (final map in values) {
for (final keyAndValue in map.entries) {
print('Will call breathe() on ${keyAndValue.key}');
keyAndValue.value.breathe();
}
}
}
mixin CanBreathe {
void breathe();
}
class Person with CanBreathe {
const Person();
@override
void breathe() {
print('Person is breathing...');
}
}
class Fish with CanBreathe {
const Fish();
@override
void breathe() {
print('Fish is breathing...');
}
}
// Part 03 - END
// Part 04 - Unconstrained Generic Type Definitions
typedef KeyValue<K, V> = MapEntry<K, V>;
// Part 04 - END
// Part 05 - Specializing Generic Type Definitions
typedef JSON<T> = Map<String, T>;
// Part 05 - END
// Part 06 - Generic Mixins and Specialized Mixin Type Definitions
mixin HasHeight<H extends num> {
H get height;
}
typedef HasIntHeight = HasHeight<int>;
typedef HasDoubleHeight = HasHeight<double>;
class Person2 with HasDoubleHeight {
@override
final double height;
const Person2({required this.height});
}
class Dog2 with HasIntHeight {
@override
final int height;
const Dog2({required this.height});
}
// Part 06 - END
// Part 07 - Generic Classes with Generic Extensions
class Tuple<L, R> {
final L left;
final R right;
const Tuple(this.left, this.right);
@override
String toString() => 'Tuple, left = $left, right = $right';
}
extension<L, R> on Tuple<L, R> {
Tuple<R, L> swap() => Tuple(right, left);
}
extension<L extends num, R extends num> on Tuple<L, R> {
num get sum => left + right;
}
// Part 07 - END
// Part 08 - Generic Sorting with Comparable
const ages = [100, 20, 10, 90, 40];
const names = ['Foo', 'Bar', 'Baz'];
const distances = [3.1, 10.2, 1.3, 4.2];
int isLessThan<T extends Comparable>(T a, T b) => a.compareTo(b);
int isGreaterThan<T extends Comparable>(T a, T b) => b.compareTo(a);
void sort({required bool ascending}) {
final comparator = ascending ? isLessThan : isGreaterThan;
print([...ages]..sort(comparator));
print([...names]..sort(comparator));
print([...distances]..sort(comparator));
}
// Part 08 - END
// Part 09 - Handling Lack of Common Ancestors
extension ToInt on Object {
int toInt() {
final list = [
if (this is Iterable<Object>)
...this as Iterable<Object>
else if (this is int)
[this as int]
else
(double.tryParse(toString()) ?? 0.0).round()
];
return list
.map(
(e) => (double.tryParse(
e.toString(),
) ??
0.0)
.round(),
)
.reduce(
(lhs, rhs) => lhs + rhs,
);
}
}
// Part 09 - END
// Part 10 - Generic Extension on Any Data Type
abstract class Thing {
final String name;
const Thing({required this.name});
}
class Person3 extends Thing {
final int age;
const Person3({
required String name,
required this.age,
}) : super(name: name);
}
class Animal extends Thing {
final String species;
const Animal({
required String name,
required this.species,
}) : super(name: name);
}
const Thing personThing = Person3(
name: 'Foo',
age: 20,
);
const Thing animalThing = Animal(
name: 'Bar',
species: 'Cat',
);
extension<T> on T {
R? mapIfOfType<E, R>(R Function(E) f) {
final shadow = this;
if (shadow is E) {
return f(shadow);
} else {
return null;
}
}
}
// Part 10 - END
// Part 11 - Generic Function Pointers
T divide<T extends num>(T lhs, T rhs) {
if (lhs is int && rhs is int) {
return lhs ~/ rhs as T;
} else {
return lhs / rhs as T;
}
}
// Part 11 - END
// Part 12 - Generic Class Properties
class Person4<T extends num> {
final T age;
const Person4({
required this.age,
});
int get ageRounded => age.round();
}
// Part 13 - END