@@ -201,24 +201,14 @@ implicitly marked `final`. It is a compile-time error to *explicitly* mark an
201
201
instance field ` final ` (since it's redundant and likely indicates confusion on
202
202
the user's part).
203
203
204
+ It is a compile-time error if an instance field is marked ` late ` . * The lazy
205
+ initialization of late fields is another kind of mutability.*
206
+
204
207
Static fields are not implicitly final.
205
208
206
209
** TODO: This seems like a potential source of confusion. Maybe implicitly final
207
210
fields are a mistake?**
208
211
209
- Any non-` late ` instance field without an initializer at its declaration is
210
- called a * value field* . These are the fields that will be used in the
211
- implementations of ` == ` , ` hashCode ` , and ` copyWith() ` .
212
-
213
- Other instance fields, those marked ` late ` or with initializers, are still
214
- implicitly final, but are not considered value fields. These can be useful for
215
- storing information in the object that isn't part of it's user-visible "value",
216
- like cached data, metadata, debug info, etc..
217
-
218
- It is a compile-time error if a value field has a private name. * Since
219
- ` copyWith() ` uses named parameters to update the fields, a private name would
220
- prevent you from updating that field's value using ` copyWith() ` .*
221
-
222
212
## Inheritance
223
213
224
214
Inheriting from stateful classes makes value semantics more complex. We have to
@@ -236,22 +226,20 @@ The rules are:
236
226
that removing ` value ` from an extensible class is a breaking API change.*
237
227
238
228
* A value class may apply mixins and implement as many interfaces as it wants.
229
+ Any mixin applied by a value class or one of its superclasses must not have
230
+ any mutable or ` late ` instance fields.
239
231
240
- This means that every instance field in a value class that requires
241
- initialization is declared by some value class, either itself or one of its
242
- value class superclasses. In other words, every field the automatically
243
- generated methods use is a value field.
244
-
245
- When referring to the * value fields* of a value class, we mean the value fields
246
- it declares and the value fields it inherits from other value classes.
232
+ ** TODO: Is the last restriction too brittle? It means that adding a private
233
+ mutable instance field to a mixin is a breaking change. Maybe value classes
234
+ shouldn't be able to apply mixins? Or should we allow ` value mixin ` ?**
247
235
248
- It is a compile-time error for a value field in a class to shadow a value field
249
- in a superclass. * Shadowing fields is never a good idea, and shadowing a value
250
- field would make it impossible to distinguish their corresponding parameters in
251
- ` copyWith() ` .*
236
+ It is a compile-time error for an instance field in a value class to shadow a
237
+ field in a superclass. * Shadowing fields is never a good idea, and shadowing a
238
+ field in a value class would make it impossible to distinguish their
239
+ corresponding parameters in ` copyWith() ` .*
252
240
253
- It's reasonable to declare a value class that has no actual value fields. This
254
- can be useful if you want it to be a base class for other value classes.
241
+ It's reasonable to declare a value class that has no actual fields. This can be
242
+ useful if you want it to be a base class for other value classes.
255
243
256
244
## Implicit const constructors
257
245
@@ -273,7 +261,7 @@ Restating the existing specification, "would be valid" means:
273
261
}
274
262
275
263
value class B {
276
- final int x = DateTime.now().second;
264
+ int x = DateTime.now().second;
277
265
278
266
B(); // Not implicitly const constructor.
279
267
}
@@ -295,7 +283,7 @@ Restating the existing specification, "would be valid" means:
295
283
}
296
284
297
285
value class B {
298
- int = DateTime.now().second;
286
+ int x = DateTime.now().second;
299
287
300
288
B() : super.noConst(); // Not implicitly const constructor.
301
289
}
@@ -330,7 +318,7 @@ creation of a value class creates a constant or not.
330
318
331
319
An instance creation expression that invokes a const constructor in a value
332
320
class can be a constant or potentially constant expression. It is a compile-time
333
- constant expression if all of its type arguments are connstant type expressions
321
+ constant expression if all of its type arguments are constant type expressions
334
322
and its arguments are constant expressions, regardless of whether the call is
335
323
preceded by `const`.
336
324
@@ -344,7 +332,7 @@ code like Flutter build methods, provided that common types like `EdgeInsets`,
344
332
### `==` and `hashCode`
345
333
346
334
Value classes have value equality, which means two instances are equal if they
347
- are the same class and all of their corresponding value fields are equal:
335
+ are the same class and all of their corresponding instance fields are equal:
348
336
349
337
```dart
350
338
value class Point(int x, int y);
@@ -357,39 +345,45 @@ main() {
357
345
```
358
346
359
347
A value class gets an implicit definition of ` == ` and ` hashCode ` with those
360
- semantics. More precisely, the ` == ` method on value class ` r ` with right operand
361
- ` o ` is defined as:
348
+ semantics. More precisely, the ` == ` method on instance ` i ` of value class
349
+ ` C<X1,...,Xn> ` with right operand ` o ` is defined as:
362
350
363
- 1 . If ` o ` is not an instance of ` r ` 's type then ` false ` .
351
+ 1 . If the runtime type of ` o ` is not a subtype of ` C<X1,...,Xn> ` then ` false ` .
364
352
365
- * ` o ` may be an instance of a _ subtype_ of ` r ` 's type. In other words, this
366
- test is implemented like ` o is R ` where ` R ` is the type of ` r ` , including
367
- any type arguments used by the receiver instance. This means that ` == ` may
368
- not be symmetric if a value class is also extended.*
353
+ * ` o ` may be an instance of a _ subtype_ of ` C ` . In other words, this test is
354
+ implemented like ` o is C<X1,...,Xn> ` . This means that ` == ` may not be
355
+ symmetric if a value class is also extended.*
369
356
370
- 2 . For each pair of corresponding value fields ` rf ` and ` of ` in unspecified
371
- order:
357
+ 2 . For each pair of corresponding instance fields ` rf ` and ` of ` declared ` C ` ,
358
+ in unspecified order:
372
359
373
- 1 . If ` rf == of ` is ` false ` then ` false ` .
360
+ 1 . Let ` rv ` be the value of field ` rf ` . * This accesses the field directly
361
+ and does not invoke any potentially overridden getter.*
374
362
375
- 3 . Else, ` true ` .
363
+ 2 . Let ` ov ` be the value of field ` of ` . * This accesses the field directly
364
+ and does not invoke any potentially overridden getter.*
376
365
377
- * The order that fields are iterated is potentially user-visible since
378
- user-defined ` == ` methods can have side effects. Most well-behaved ` == `
379
- implementations are pure. The order that fields are visited is deliberately left
380
- unspecified so that implementations are free to reorder the field comparisons
381
- for performance.*
366
+ 3 . If ` rv == ov ` is ` false ` then ` false ` .
367
+
368
+ * The order that fields are iterated is potentially user-visible since
369
+ user-defined ` == ` methods can have side effects. Most well-behaved ` == `
370
+ implementations are pure. The order that fields are visited is deliberately
371
+ left unspecified so that implementations are free to reorder the field
372
+ comparisons for performance.*
373
+
374
+ 3 . If ` C ` has a supertype other than ` Object ` , then the result of
375
+ ` super.==(o) ` . * Since a value class may have a value superclass with private
376
+ fields that are inaccessible to ` C ` , we rely on chaining to the superclass
377
+ implementation of ` == ` to compare those fields. We don't do this if the
378
+ superclass is ` Object ` , because ` Object ` 's implementation of ` == ` isn't
379
+ well-defined for value classes since they have no defined identity.*
380
+
381
+ 3 . Else, ` true ` .
382
382
383
383
The implementation of ` hashCode ` follows this. The hash code returned should
384
384
depend on the field values such that two instances that compare equal must have
385
385
the same hash code.
386
386
387
- * Note that a value class that inherits from another value class tests its
388
- inherited fields directly instead of relying on calling ` super.==() ` or
389
- ` super.hashCode ` . This distinction mostly doesn't matter but is potentially
390
- user visible given that there may be user-defined implementations of those
391
- methods in the inheritance chain.*
392
-
393
387
### ` copyWith() `
394
388
395
389
Since value classes are immutable, to "modify" one requires constructing a new
@@ -407,17 +401,17 @@ only a few of them. To make that easier, a value class also automatically gets a
407
401
method named ` copyWith() ` that produces a copy of the current instance with
408
402
some values changed.
409
403
410
- The generated ` copyWith() ` takes a named parameter for each of the class's value
411
- fields. The parameter has the same name as the field and the parameter's type is
412
- the type of the corresponding field.
404
+ The generated ` copyWith() ` takes a named parameter for each public instance
405
+ field defined in the value class or any of its value superclasses. Each
406
+ parameter has the same name and type as the corresponding field.
413
407
414
408
The method returns a new instance of the same type as its receiver. The new
415
409
instance's field values are initialized with the values of the corresponding
416
410
arguments. If no argument is passed, then the current instance's field value is
417
411
used instead.
418
412
419
413
* If the underlying field is itself nullable, the generated ` copyWith() ` still
420
- distinguishes between a passed argument overriding the value even if that value
414
+ distinguishes between a passed argument replacing the value even if that value
421
415
is ` null ` . For example:*
422
416
423
417
```
@@ -449,9 +443,34 @@ class MaybeInt {
449
443
* Supporting non-const default values is not needed for this proposal.*
450
444
451
445
The new instance created by ` copyWith() ` does not invoke any user-defined
452
- constructor on the value class. It's as if each value class has an implicit
446
+ constructor on the value class. * It's as if each value class has an implicit
453
447
hidden constructor used only by ` copyWith() ` that initializes all of its fields
454
- and calls the corresponding hidden constructor on any value superclass.
448
+ and calls the corresponding hidden constructor on any value superclass. This
449
+ hidden constructor copies the values of any private instance fields to the new
450
+ instance:*
451
+
452
+ ``` dart
453
+ value class A {
454
+ int _x;
455
+
456
+ A(this._x);
457
+ }
458
+
459
+ value class B extends A {
460
+ int _y;
461
+ int z;
462
+
463
+ B(super._x, this._y, this.z);
464
+
465
+ String toString() => 'B($_x, $_y, $z)';
466
+ }
467
+
468
+ main() {
469
+ var b = B(1, 2, 3);
470
+ var b2 = b.copyWith(z: 4);
471
+ print(b2); // Prints "B(1, 2, 4)".
472
+ }
473
+ ```
455
474
456
475
### Explicit implementations
457
476
0 commit comments