Skip to content

Commit 84b6dc1

Browse files
"Specify members" and "access state" for mixins (#5694)
fixes #4871
1 parent 008db87 commit 84b6dc1

File tree

1 file changed

+116
-50
lines changed

1 file changed

+116
-50
lines changed

src/content/language/mixins.md

+116-50
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ Mixins are a way of defining code that can be reused in multiple class hierarchi
1616
They are intended to provide member implementations en masse.
1717

1818
To use a mixin, use the `with` keyword followed by one or more mixin
19-
names. The following example shows two classes that use mixins:
19+
names. The following example shows two classes that use (or, are subclasses of)
20+
mixins:
2021

2122
<?code-excerpt "misc/lib/language_tour/classes/orchestra.dart (musician-and-maestro)" replace="/(with.*) \{/[!$1!] {/g"?>
2223
```dart
@@ -60,30 +61,120 @@ mixin Musical {
6061
}
6162
```
6263

63-
Sometimes you might want to restrict the types that can use a mixin.
64-
For example, the mixin might depend on being able to invoke a method
65-
that the mixin doesn't define.
66-
As the following example shows, you can restrict a mixin's use
67-
by using the `on` keyword to specify the required superclass:
64+
## Specify members a mixin can call on itself
65+
66+
Sometimes a mixin depends on being able to invoke a method or access fields,
67+
but can't define those members itself (because mixins can't use constructor
68+
parameters to instantiate their own fields).
69+
70+
The following sections cover different strategies for ensuring any subclass
71+
of a mixin defines any members the mixin's behavior depends on.
72+
73+
### Define abstract members in the mixin
74+
75+
Declaring an abstract method in a mixin forces any type that uses
76+
the mixin to define the abstract method upon which its behavior depends.
77+
78+
```dart
79+
mixin Musician {
80+
void playInstrument(String instrumentName); // Abstract method.
81+
82+
void playPiano() {
83+
playInstrument('Piano');
84+
}
85+
void playFlute() {
86+
playInstrument('Flute');
87+
}
88+
}
89+
90+
class Virtuoso with Musician {
91+
void playInstrument(String instrumentName) { // Subclass must define.
92+
print('Plays the $instrumentName beautifully');
93+
}
94+
}
95+
```
96+
97+
#### Access state in the mixin's subclass
98+
99+
Declaring abstract memebers also allows you to access state on the subclass
100+
of a mixin, by calling getters which are defined as abstract on the mixin:
101+
102+
```dart
103+
/// Can be applied to any type with a [name] property and provides an
104+
/// implementation of [hashCode] and operator `==` in terms of it.
105+
mixin NameIdentity {
106+
String get name;
107+
108+
int get hashCode => name.hashCode;
109+
bool operator ==(other) => other is NameIdentity && name == other.name;
110+
}
111+
112+
class Person with NameIdentity {
113+
final String name;
114+
115+
Person(this.name);
116+
}
117+
```
118+
119+
### Implement an interface
120+
121+
Similar to declaring the mixin abstract, putting an `implements` clause on the
122+
mixin while not actually implementing the interface will also ensure any member
123+
dependencies are defined for the mixin.
124+
125+
```dart
126+
abstract interface class Tuner {
127+
void tuneInstrument();
128+
}
129+
130+
mixin Guitarist implements Tuner {
131+
void playSong() {
132+
tuneInstrument();
133+
134+
print('Strums guitar majestically.');
135+
}
136+
}
137+
138+
class PunkRocker with Guitarist {
139+
void tuneInstrument() {
140+
print("Don't bother, being out of tune is punk rock.");
141+
}
142+
}
143+
```
144+
145+
### Use the `on` clause to declare a superclass
146+
147+
The `on` clause exists to define the type that `super` calls are resolved against.
148+
So, you should only use it if you need to have a `super` call inside a mixin.
149+
150+
The `on` clause forces any class that uses a mixin to also be a subclass
151+
of the type in the `on` clause.
152+
If the mixin depends on members in the superclass,
153+
this ensures those members are available where the mixin is used:
68154

69-
<?code-excerpt "misc/lib/language_tour/classes/orchestra.dart (mixin-on)" plaster="none" replace="/on Musician2/[!on Musician!]/g"?>
70155
```dart
71156
class Musician {
72-
// ...
157+
musicianMethod() {
158+
print('Playing music!');
159+
}
73160
}
74161
75162
mixin MusicalPerformer [!on Musician!] {
76-
// ...
163+
perfomerMethod() {
164+
print('Performing music!');
165+
super.musicianMethod();
166+
}
77167
}
78-
class SingerDancer extends Musician with MusicalPerformer {
79-
// ...
168+
169+
class SingerDancer extends Musician with MusicalPerformer { }
170+
171+
main() {
172+
SingerDance().performerMethod();
80173
}
81174
```
82175

83-
In the preceding code,
84-
only classes that extend or implement the `Musician` class
85-
can use the mixin `MusicalPerformer`.
86-
Because `SingerDancer` extends `Musician`,
176+
In this example, only classes that extend or implement the `Musician` class
177+
can use the mixin `MusicalPerformer`. Because `SingerDancer` extends `Musician`,
87178
`SingerDancer` can mix in `MusicalPerformer`.
88179

89180
## `class`, `mixin`, or `mixin class`?
@@ -96,49 +187,24 @@ A `mixin` declaration defines a mixin. A `class` declaration defines a [class][]
96187
A `mixin class` declaration defines a class that is usable as both a regular class
97188
and a mixin, with the same name and the same type.
98189

99-
Any restrictions that apply to classes or mixins also apply to mixin classes:
100-
101-
- Mixins can't have `extends` or `with` clauses, so neither can a `mixin class`.
102-
- Classes can't have an `on` clause, so neither can a `mixin class`.
103-
104-
### `abstract mixin class`
105-
106-
You can achieve similar behavior to the `on` directive for a mixin class.
107-
Make the mixin class `abstract` and define the abstract methods its behavior
108-
depends on:
109-
110190
```dart
111-
abstract mixin class Musician {
112-
// No 'on' clause, but an abstract method that other types must define if
113-
// they want to use (mix in or extend) Musician:
114-
void playInstrument(String instrumentName);
115-
116-
void playPiano() {
117-
playInstrument('Piano');
118-
}
119-
void playFlute() {
120-
playInstrument('Flute');
121-
}
191+
mixin class Musician {
192+
// ...
122193
}
123194
124-
class Virtuoso with Musician { // Use Musician as a mixin
125-
void playInstrument(String instrumentName) {
126-
print('Plays the $instrumentName beautifully');
127-
}
128-
}
195+
class Novice with Musician { // Use Musician as a mixin
196+
// ...
197+
}
129198
130199
class Novice extends Musician { // Use Musician as a class
131-
void playInstrument(String instrumentName) {
132-
print('Plays the $instrumentName poorly');
133-
}
134-
}
200+
// ...
201+
}
135202
```
136203

137-
By declaring the `Musician` mixin as abstract, you force any type that uses
138-
it to define the abstract method upon which its behavior depends.
204+
Any restrictions that apply to classes or mixins also apply to mixin classes:
139205

140-
This is similar to how the `on` directive ensures a mixin has access to any
141-
interfaces it depends on by specifying the superclass of that interface.
206+
- Mixins can't have `extends` or `with` clauses, so neither can a `mixin class`.
207+
- Classes can't have an `on` clause, so neither can a `mixin class`.
142208

143209
[language version]: /guides/language/evolution#language-versioning
144210
[class]: /language/classes

0 commit comments

Comments
 (0)