1
0
mirror of https://github.com/dart-lang/sdk synced 2024-07-03 00:08:46 +00:00

Add extension types to the CHANGELOG.

Change-Id: Id3bf8bd8b0a366ce58eb9070f0e2fa15d55bf07d
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/350002
Reviewed-by: Alexander Thomas <athom@google.com>
Reviewed-by: Leaf Petersen <leafp@google.com>
Commit-Queue: Alexander Thomas <athom@google.com>
Reviewed-by: Erik Ernst <eernst@google.com>
Auto-Submit: Bob Nystrom <rnystrom@google.com>
This commit is contained in:
Erik Ernst 2024-02-12 08:20:47 +00:00 committed by Commit Queue
parent 4c8ae57d93
commit f9266472c0

View File

@ -66,6 +66,97 @@
### Language
Dart 3.3 adds [extension types] to the language. To use them, set your
package's [SDK constraint][language version] lower bound to 3.3 or greater
(`sdk: '^3.3.0'`).
#### Extension types
[extension types]: https://github.com/dart-lang/language/issues/2727
An _extension type_ wraps an existing type with a different, static-only
interface. It works in a way which is in many ways similar to a class that
contains a single final instance variable holding the wrapped object, but
without the space and time overhead of an actual wrapper object.
Extension types are introduced by _extension type declarations_. Each
such declaration declares a new named type (not just a new name for the
same type). It declares a _representation variable_ whose type is the
_representation type_. The effect of using an extension type is that the
_representation_ (that is, the value of the representation variable) has
the members declared by the extension type rather than the members declared
by its "own" type (the representation type). Example:
```dart
extension type Meters(int value) {
String get label => '${value}m';
Meters operator +(Meters other) => Meters(value + other.value);
}
void main() {
var m = Meters(42); // Has type `Meters`.
var m2 = m + m; // OK, type `Meters`.
// int i = m; // Compile-time error, wrong type.
// m.isEven; // Compile-time error, no such member.
assert(identical(m, m.value)); // Succeeds.
}
```
The declaration `Meters` is an extension type that has representation type
`int`. It introduces an implicit constructor `Meters(int value);` and a
getter `int get value`. `m` and `m.value` is the very same object, but `m`
has type `Meters` and `m.value` has type `int`. The point is that `m`
has the members of `Meters` and `m.value` has the members of `int`.
Extension types are entirely static, they do not exist at run time. If `o`
is the value of an expression whose static type is an extension type `E`
with representation type `R`, then `o` is just a normal object whose
run-time type is a subtype of `R`, exactly like the value of an expression
of type `R`. Also the run-time value of `E` is `R` (for example, `E == R`
is true). In short: At run time, an extension type is erased to the
corresponding representation type.
A method call on an expression of an extension type is resolved at
compile-time, based on the static type of the receiver, similar to how
extension method calls work. There is no virtual or dynamic dispatch. This,
combined with no memory overhead, means that extension types are zero-cost
wrappers around their representation value.
While there is thus no performance cost to using extension types, there is
a safety cost. Since extension types are erased at compile time, run-time
type tests on values that are statically typed as an extension type will
check the type of the representation object instead, and if the type check
looks like it tests for an extension type, like `is Meters`, it actually
checks for the representation type, that is, it works exactly like `is int`
at run time. Moreover, as mentioned above, if an extension type is used as
a type argument to a generic class or function, the type variable will be
bound to the representation type at run time. For example:
```dart
void main() {
var meters = Meters(3);
// At run time, `Meters` is just `int`.
print(meters is int); // Prints "true".
print(<Meters>[] is List<int>); // Prints "true".
// An explicit cast is allowed and succeeds as well:
List<Meters> meterList = <int>[1, 2, 3] as List<Meters>;
print(meterList[1].label); // Prints "2m".
}
```
Extension types are useful when you are willing to sacrifice some run-time
encapsulation in order to avoid the overhead of wrapping values in
instances of wrapper classes, but still want to provide a different
interface than the wrapped object. An example of that is interop, where you
may have data that are not Dart objects to begin with (for example, raw
JavaScript objects when using JavaScript interop), and you may have large
collections of objects where it's not efficient to allocate an extra object
for each element.
#### Other changes
- **Breaking Change** [#54056][]: The rules for private field promotion have
been changed so that an abstract getter is considered promotable if there are
no conflicting declarations. There are no conflicting declarations if