dart method calling context dart method calling context dart dart

dart method calling context


In Dart there is a difference between an instance-method, declared as part of a class, and other functions (like closures and static functions).

Instance methods are the only ones (except for constructors) that can access this. Conceptually they are part of the class description and not the object. That is, when you do a method call o.foo() Dart first extracts the class-type of o. Then it searches for foo in the class description (recursively going through the super classes, if necessary). Finally it applies the found method with this set to o.

In addition to being able to invoke methods on objects (o.foo()) it is also possible to get a bound closure: o.foo (without the parenthesis for the invocation). However, and this is crucial, this form is just syntactic sugar for (<args>) => o.foo(<args>). That is, this just creates a fresh closure that captures o and redirects calls to it to the instance method.

This whole setup has several important consequences:

  • You can tear off instance methods and get a bound closure. The result of o.foo is automatically bound to o. No need to bind it yourself (but also no way to bind it to a different instance). This is way, in your example, one.getMyId works. You are actually getting the following closure: () => one.getMyId() instead.

  • It is not possible to add or remove methods to objects. You would need to change the class description and this is something that is (intentionally) not supported.

  • var f = o.foo; implies that you get a fresh closure all the time. This means that you cannot use this bound closure as a key in a hashtable. For example, register(o.foo) followed by unregister(o.foo) will most likely not work, because each o.foo will be different. You can easily see this by trying print(o.foo == o.foo).

  • You cannot transfer methods from one object to another. However you try to access instance methods, they will always be bound.


Looking at your examples:

  print('one ${caller(one.getMyId)}');             //one 1  print('two ${caller(two.getMyId)}');             //two 2  print('one ${callerJustForThree(one.getMyId)}'); //NoSuchMethod Exception

These lines are equivalent to:

 print('one ${caller(() => one.getMyId())}'); print('two ${caller(() => two.getMyId())}'); print('one ${callerJustForThree(() => one.getMyId())}';

Inside callerJustForThree:

callerJustForThree(fn){  var three = new IDable(3);  three.fn();}

The given argument fn is completely ignored. When doing three.fn() in the last line Dart will find the class description of three (which is IDable) and then search for fn in it. Since it doesn't find one it will call the noSuchMethod fallback. The fn argument is ignored.

If you want to call an instance member depending on some argument you could rewrite the last example as follows:

main() {  ...  callerJustForThree((o) => o.getMyId());}callerJustForThree(invokeIDableMember){  var three = new IDable(3);  invokeIDableMember(three);}


I'll try to explain, which is not necessarily a strength of mine. If something I wrote isn't understandable, feel free to give me a shout.

Think of methods as normal objects, like every other variable, too.

When you call caller(one.getMyId), you aren't really passing a reference to the method of the class definition - you pass the method "object" specific for instance one.

In callerJustForThree, you pass the same method "object" of instance one. But you don't call it. Instead of calling the object fn in the scope if your method, you are calling the object fn of the instance three, which doesn't exist, because you didn't define it in the class.

Consider this code, using normal variables:

void main() {  var one = new IDable(1);  var two = new IDable(2);  caller(one.id);  caller(two.id);  callerJustForThree(one.id);}class IDable{  int id;  IDable(this.id);}caller(param){  print(param);}callerJustForThree(param){  var three = new IDable(3);  print(three.id); // This works  print(param);   // This works, too  print(three.param); // But why should this work?}

It's exactly the same concept. Think of your callbacks as normal variables, and everything makes sense. At least I hope so, if I explained it good enough.