I'd like to use noSuchMethod to reduce boilerplate when writing a lot of similar methods. But then the API is worse for my library's users because code completion no longer works. I tried this but I get warnings about unimplemented methods:
class ThingMixin {
foo();
bar();
noSuchMethod(Invocation inv) {
...
}
}
Is there a workaround?
The trick is to define the API in a separate class:
abstract class ThingApi {
foo();
bar();
}
class ThingMixin implements ThingApi {
noSuchMethod(Invocation inv) {
...
}
}
(Note that noSuchMethod cannot call super if you want it to work as a mixin.)
Related
Say I have a package that exposes a mixin that provides some extensibility through an API:
mixin ListenerModifier<T> {
T get value;
void addListener(Callback callback);
void removeListener(Callback callback);
}
Say I want to make a "plugin" based on this mixin:
mixin PreviousValue<T> on ListenerModifier<T> {
late T previous;
const PreviousValue() {
addListener(() => previous = value);
}
}
(You could also have plugins defined in other packages as well, perhaps to expose a Stream<T> getter.)
Then, a user could use any plugins at will, like this:
abstract class MyListener<T> with ListenerModifier<T>, PreviousValue<T> {}
The issue is, mixins can't have constructors like the above PreviousValue assumes. Is there some OOP/architectural way to get around this problem? I initially thought of just forcing users of PreviousValue (for example) to call a registerPreviousValue() method in their MyListener constructor as a workaround, but that is highly error prone.
Related:
How to group mixins in Dart?
How can I initialize a mixin's immutable data in Dart?
Just realized for the particular example I gave, I can use a different sort of plugin system without mixins:
mixin ListenerModifierPluginAPI {
T get value;
void addListener(Callback callback);
void removeListener(Callback callback);
}
abstract class ListenerModifier with ListenerModifierPluginAPI {
T registerPlugin<T>(T Function(ListenerModifierPluginAPI) plugin) => plugin(this);
}
class MyListenerModifier extends ListenerModifier {
late final PreviousValueState previousValueState;
const MyListenerModifier() {
previousValueState = registerPlugin(previousValuePlugin);
}
// ...
}
This may not work for all people though. If someone has a different solution, I am all ears.
Is it possible to call a second ancestor method in dart? something like super.super.hello()? If it's possible, how can it be written?
class A {
void hello() {
print('A');
}
}
class B extends A {
#override
void hello() {
print('B');
}
}
class C extends B {
#override
void hello() {
// How to call the hello() implemented in the class A here?
}
}
void main() {
var c = C();
c.hello();
}
It's not possible.
The reason it's not possible is that it breaks abstraction.
When you look at class C extend B, all you need to know about B is which signatures its members has and which interfaces it implements. As long as that stays effectively the same, your valid code will keep working.
Consider what would happen if the author of B decided to make themselves a helper base-class:
abstract class _BaseB extends A {
String get _myName;
#override
void hello() {
print(_myName);
}
}
class B extends _BaseB {
#override
String get _myName => "B";
}
That's a perfectly valid refactoring. The resulting class B has all the same members and implements all the same interfaces (and also _BaseB, but it's private so nobody can see that).
The C class above would keep working if all it does is to call super.hello(). If it had a way to ask for super.super.hello(), that might no longer be valid.
Similarly if the B class was changed to:
class B implements A {
#override
void hello() {
print("B");
}
}
(changing extends to implements), then all methods of B works the same as before and it implements the same interfaces. Again, there is no visible differences to the users of the B class.
But if you could call something like A.super.hello() to reach the A class's hello method, then that would now break because that method isn't in the B class at all.
So, by restricting super.hello() to only call methods on the precise class you write as the superclass, you are prevented from introducing dependencies on the implementation of B, dependencies which would make otherwise valid refactorings into breaking changes.
I have the following code:
class A {
void m() {
print("hello");
}
}
mixin B {
void m() {
print("mixin class b");
}
}
class C extends A with B {
void m() {
print("m of c");
super.m();
}
}
void main() {
C cc = C();
cc.m();
}
Here when I write super.m() it is referring to the mixin class rather than m() of class A, why? Which exactly is its superclass referenced by super.
How can I then call m() of class A from C?
I don't think it is possible to get the m method on A in your example. The reason is by using mixins you are telling the compiler you want to extend A but please insert all methods from B even if one of the methods overrides an existing one. So after compile, the compiled class of C ends up not even containing A.m.
There are some details about this behavior in this older article and with some explanation about why this is the case: https://medium.com/flutter-community/https-medium-com-shubhamhackzz-dart-for-flutter-mixins-in-dart-f8bb10a3d341
I would like to use the nested Generics, like
class Class<List<T>> {
...
}
But always Dart Editor gives me alerts. How should I avoid these alerts?
Well, Dart Editor is right. This code doesn't make any sense. Without further information on what you are trying to do (don't hesitate to update your question), I am assuming you actually mean one of those:
class MyClass<T> {
List<T> listField;
// other stuff
}
Or maybe the list itself should be generic?
void main() {
MyClass<SomeCustomListClass<String>> instance = new MyClass();
}
class MyClass<T extends List<String>> {
T listField;
// ...
}
Or maybe everything has to be generic:
void main() {
MyClass<String, SomeCustomListClass<String>> instance = new MyClass();
}
class MyClass<TElement, TList extends List<TElement>> {
TList listField;
TElement _firstListElement;
// whatever that could be used for
}
I've got a grails app with Service classes that inherit from Groovy's GroovyInterceptable:
class customerSerrvice implements GroovyInterceptable {
private List<Customer> customers
def invokeMethod(String name, args) {
log.debug "=======>INVOKING method [$name] with args:$args"
}
void foo() {
customers.each { doSomething(it) }
}
void doSomething(Customer cust) { log.debug "doSomething invoked with $cust" }
}
The above is a greatly simplified representation, but it gives you the idea. If I call foo() or doSomething() directly from another class, the invokeMethod gets called like it is supposed to. However, when foo() calls doSomething(), that call is not intercepted in invokeMethod.
If I change from
customers.each { doSomething(it) }
to
for(Customer cust: customers) { doSomething(cust) }
then the invokeMethod gets called just fine.
So is there something about closures and GroovyInterceptable that don't go together? Is there any way to get the invokeMethod to work with closures short of changing them all out?
Thanks
Confirmed as a bug, old link:
http://jira.codehaus.org/browse/GROOVY-4610, new link:
https://issues.apache.org/jira/browse/GROOVY-4610