Question

I would like to define a getter that can optionally take arguments. I managed to achieve that, however it only works if I put the obligatory () after the call. Here's the code:

get children => ([role=null]) {
  if(role == null || role == 'any') { return _children;               }
  else                              { return _children_by_role[role]; }
};

So now I can say

obj.children('something').length;

or

obj.children().length;

but I cannot say

obj.children; // this doesn't work

because it results in the following error:

Caught Closure call with mismatched arguments: function 'length' NoSuchMethodError : method not found: 'length' Receiver: Closure: ([dynamic])
Was it helpful?

Solution

In Dart, getters are meant to be indistinguishable from accessing object properties, so it is illegal to define a getter that accepts an argument (even if it's optional).

Your getter takes no arguments, but with the => operator, returns an anonymous function that takes an optional argument. So, obj.children is a function; therefore the statement obj.children.length; is an error because functions don't have the property length.

You may not be able to omit the parentheses, but your code would work more naturally if get children wasn't a getter function:

getChildren([roll]) { // null is the default value implicitly
  if (roll == null || roll == 'any') return _children;
  else return _children_by_roll[roll];
}

used as:

obj.getChildren().length;

or:

obj.getChildren(rollObject).length;

OTHER TIPS

Getters are not allowed to have any arguments.

You've managed to get around that by returning a closure instead but that mandates the () syntax (in which case it is not much better than a regular function).

If you are really determined to use the getter syntax you can get almost everything you want by using a combination of function emulation and delegation:

class ChildrenGetter {

  List _children;

  ChildrenGetter(this._children);

  // Function emulation: will be called whenever () is used on
  // a ChildrenGetter instance, e.g. childrenGetter();
  call([role = null]) {
    if(role == null || role == 'any') { return _children;               }
    // Omitted for scope of example, you should be able to 
    // modify to have this work
    //else                              { return _children_by_role[role]; }
  }

  // Delegate length
  get length => _children.length;

  // Delegate every other operator, method, getter, setter... 

}

And now your getter can be defined as:

get children => new ChildrenGetter(_children);

(In actual code you wouldn't want to return a new instance each time, but for the sake of the example, this was easiest.)

You can now call any of the following and receive proper results:

obj.children();
obj.children.length;        
obj.children('any').length;

Since you are returning a class that acts as a function when called with () but that can also have getters, setters, methods and operators that delegate to the actual children.

The one problem you will have is that obj.children will refer to the instance of ChildrenGetter and not to the delegated children.

I'm not sure I would recommend this approach, unless there is a good reason for it, but it's interesting to note that it can be done.

Getters don't take arguments. You should just define a function with one or more default arguments to achieve what you want.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top