Properties

Properties are a specific way to declare class fields, and can be used to implement several kinds of features such as read-only/write-only fields or fields accessed through getter-setter methods.

Here's a property declaration example :

class C {

 public var x(get,set) : Int;

 function get_x(){ return 123; } 

 function set_x(value){ 
   doSomethingWith(value); 
   return 123; 
 } 
}

The values for get and set can be one of the following :

  • a get or set keywords, which means the corresponding method must exist (in our example these are "get_x" and "set_x" functions)
  • null if the access is restricted from anywhere but its class' methods (see more below)
  • default if the access is a classic field access
  • dynamic if the access is done through a runtime-generated method
  • never if the access is never allowed, not even by means of Reflection

Sample

Here's a complete example:

class C {
    public var ro(default,null) : Int;
    //Can be read from anywhere, modified only within the class

    public var wo(null,default) : Int;
    //Can be set from anywhere, only read within the class

    public var x(get,set) : Int;
    //Can be read from and written to  from any class; under the hood, a pair of private methods is used

    public var y(get,never) : Int; 
    //Can be read from anywhere, but never modified, not even within the class

    public var z(default, set) : Int;
    //Publicly readable and writable, but the setter uses a method

    private var my_x : Int;

    //You can inline getters and setters
    private inline function get_x():Int {  
        return my_x;
    }

    // here y should always equal x
    private function get_y():Int {
    //The name of the getter or setter must be in the form of "get/set_<property name>()"
        return my_x;
    }

    private function set_x( v : Int ):Int {
        if( v >= 0 )
            my_x = v;
        return my_x;
    }

    private function set_z( v : Int ) : Int {
        return z = v;
    }
}

Using property declarations, we declare five public fields in the class C :

  • outside the class code, the field ro is read only
  • outside the class code, the field wo is write only
  • the field x is accessed through a pair of getter/setter methods
  • the field y can never be set, not even from code within the same class
  • the field "z" calls the method "set_z" when "z" is set

For instance, the following two functions are equivalent, although the methods get_x and set_x are private and then can't be accessed directly as in f2 :

    var c : C;
    function f1() {
        c.x *= 2;
    }
    function f2() {
        c.set_x(c.get_x()*2);
    }

Important Remarks

Getter and setter methods only work if the type of the class is known. There is no runtime properties handling, so for instance the following code will always trace null since the method get_x will never be called :

    var c : Dynamic = new C();
    trace(c.x);

The same result will come from trying to use a property with a getter method from a template.

Similarly, read-only and write-only properties will be both readable and writable using a dynamic variable since the type of the class is unknown.

Also, please note that you have to return a value from the setter function. The compiler will complain otherwise.

Dynamic Property

The additional dynamic access can be used to add methods at runtime, on the condition that the containing class implements Dynamic. When a dynamic field x is accessed for reading, the get_x method is called, when accessed for writing the set_x method is called :

class C implements Dynamic {
    public var age(dynamic,dynamic) : Int;
    public function new() {
    }
}

class Test {
    static function main() {
        var c = new C();
        var my_age = 26;
        Reflect.setField(c,"get_age",function() { return my_age; });
        Reflect.setField(c,"set_age",function(a) { my_age = a; return my_age; });
        trace(c.age); // 26
        c.age++; // will call c.set_age(c.get_age()+1)
        trace(c.age); // 27
    }
}

Null Property

Unlike initial expectations, null setter specification does not deny all access to the property, as is evidenced with the following example, which indeed succeeds to set the propertys value from within any class' own method(s):

class Main
{
     static function main()
     {
          var foo = new Foo();
          foo.prop = 1; /// Naturally, fails - write access denied from here.
          Foo.set_prop_value(foo, 1); /// Succeeds.
     }
}

class Foo
{
     static public function set_prop_value(foo: Foo, value: Int)
     {
          foo.prop = value; /// Will actually set the value of 'prop' indeed.
     }

     public var prop(default, null): Int;
     
     public function new() { }
}

So, unlike specifying the setter as 'never' (more on that below), 'null' only restricts access from outside the class definition scope, i.e. all scopes eligible for "public" access only. If you need strictly read-only properties, regardless access context, use 'never'.

Never Property

This property non-access specifier can be useful in a number of cases. Below is an example where the number of stored List items is returned by the numItems property. Of course, the return value should always be read from the List and never be set explicitly..

class Container {

    private var items : List<Item>;
    public var numItems( get, never ) : Int;
    function get_numItems() : Int {
        return items.length;
    }

    public function new() {
        items = new List();
        items.add( new Item() );
        items.add( new Item() );
    }
}
Now, if called after adding the items in the constructor, both numItems = 3; and Reflect.setField(this, "numItems", 3); would fail because of the never access specified. On the other hand, if null was specified instead of never, the compiler would have allowed the Reflect call. This could mislead the developer into property abuse.

«« Iterators | Optional Arguments »»

version #19736, modified 2013-09-07 01:32:12 by MrWint