Mixins is all about how a class does what it does, it's inheriting and sharing concrete implementation.
Interfaces is all about what a class is, it is the abstract signature and promises that the class must satisfy. It's a type.
Take a class that is implemented as class MyList<T> extends Something with ListMixin<T> ...
. You can use this class as MyList<int> l = new MyList<int>();
or List<int> l = new MyList<int>()
, but you should never write ListMixin<int> l = new MyList<int>()
. You can, but you shouldn't, because that is treating ListMixin
as a type, and it really isn't intended as one.
It's the same reason you should always write Map m = new HashMap();
and not HashMap m = new HashMap();
- the type is Map
, it's an implementation detail that it's a HashMap
.
If you mix in a class (or rather, the mixin derived from a class), then you get all the concrete members of that class in your new mixin class.
If you implement a class (or rather, the implicit interface of a class), then you get no concrete members at all, but the abstract signature becomes part of your interface.
Some classes can be used as both, but you should only ever use a class as a mixin if it is intended to be used as a mixin (and documented as such). There are many changes that an class author can do to a class that would break their use as a mixin. We don't want to disallow any such change, which could be perfectly reasonable changes for a non-mixin class, so using a non-mixin class as a mixin is fragile and likely to break in the future.
On the other hand, a class intended to be used as a mixin is usually all about implementation, so it is likely that there is a similar interface declared as well, and that's what you should use in the implements clause.
So, if you want to implement a list, you can either implement the List
class and do all the implementation yourself, or mix in the ListMixin
class to reuse some base functionality. You can still write implements List<T>
, but you get that by inheritance from ListMixin
.
Mixins is not a way to get multiple inheritance in the classical sense.
Mixins is a way to abstract and reuse a family of operations and state.
It is similar to the reuse you get from extending a class, but it is compatible with single-inheritance because it is linear.
If you have multiple inheritance, your class has two (or more) superclasses, and you need to handle conflicts between them, including diamond inheritance, in some way.
Mixins in Dart work by creating a new class that layers the implementation of the mixin on top of a superclass to create a new class - it is not "on the side" but "on top" of the superclass, so there is no ambiguity in how to resolve lookups.
Example:
class Counter {
int _counter = 0;
int next() => ++_counter;
}
class Operation {
void operate(int step) { doSomething(); }
}
class AutoStepOperation extends Operation with Counter {
void operate([int step]) {
super.operate(step ?? super.next());
}
}
What really happens is that you create a new class "Operation with Counter". It's equivalent to:
Example:
class Counter {
int _counter = 0;
int next() => ++_counter;
}
class Operation {
void operate(int step) { doSomething(); }
}
class $OperationWithCounter = Operation with Counter;
class AutoStepOperation extends $OperationWithCounter {
void operate([int step]) {
super.operate(step ?? super.next());
}
}
The mixin application of Counter
to Operation
create a new class, and that class appears in the superclass chain of AutoStepOperation
.
If you do class X extends Y with I1, I2, I3 { ... }
then you create four classes. If you just do class X extends Y implements I1, I2, I3 { ... }
then you only create one class. Even if all of I1
, I2
and I3
are completely empty abstract interfaces, using with
to apply them is equivalent to:
class $X1 extends Y implements I1 { /* no members in I1 */ }
class $X2 extends $X1 implements I2 { /* no members in I2 */ }
class $X3 extends $X2 implements I3 { /* no members in I3 */ }
class X extends $X3 { /* members of X */ }
You wouldn't write that directly, so you shouldn't write it using with
either