5 | | Properties provide a way to get and set the values that characterize an object. A business application could have a Customer class with properties .name, .address and .id. A video game could have a !GameObject class with properties .position, .velocity and .health. |
| 5 | Properties provide a way to get and set values that characterize an object. These values can be private fields or entirely code generated or synthesized. These give class members that provide a mechanism to read, write, or compute the value of a real or virtual private field. |
| 6 | Properties can be used as if they are public data members, but the private field access can be mediated by code |
| 7 | |
| 8 | Properties combine aspects of both fields and methods. To the user of an object, a property appears to be a field, accessing the property requires the same syntax. To the implementer of a class, a property is one or two code blocks, representing a get accessor and/or a set accessor. The code block for the get accessor is executed when the property is read; the code block for the set accessor is executed when the property is assigned a new value. A property without a set accessor is considered read-only. A property without a get accessor is considered write-only. A property that has both accessors is read-write. |
| 9 | |
| 10 | Cobra provides some shortcut syntax for the most common case - property access to a private backgrounding variable and for marking readonly ('''get''') and writeonly ('''set''') properties clearly. |
| 11 | |
| 12 | A class properties can be indexed as well as single variable access. |
8 | | |
9 | | to-do |
| 15 | {{{ |
| 16 | pro NAME from var [ is ACCESSMODIFIER ] (1) |
| 17 | pro NAME from VARNAME [ is ACCESSMODIFIER ] (2) |
| 18 | pro NAME from var [ as TYPE ] [ is ACCESSMODIFIER ] [ = INITEXPR ] (3) |
| 19 | |
| 20 | pro NAME [as RETURNTYPE] [is ACCESSMODIFIER ] (4) |
| 21 | [ATTRIBUTE] |
| 22 | [DOCSTRING] |
| 23 | [ get |
| 24 | GETBLOCK ] |
| 25 | [ set |
| 26 | SETBLOCK ] |
| 27 | |
| 28 | # shortcut: readonly properties |
| 29 | get NAME from var (1) |
| 30 | get NAME from VARNAME (2) |
| 31 | get NAME as RETURNTYPE is ACCESSMODIFIER (3) |
| 32 | get NAME [as RETURNTYPE] [is ACCESSMODIFIER ] (4) |
| 33 | [ATTRIBUTE] |
| 34 | [DOCSTRING] |
| 35 | GETBLOCK |
| 36 | |
| 37 | # shortcut: write-only properties |
| 38 | set NAME from var (1) |
| 39 | set NAME from VARNAME (2) |
| 40 | set NAME as RETURNTYPE is ACCESSMODIFIER (3) |
| 41 | set NAME [as RETURNTYPE] [is ACCESSMODIFIER ] (4) |
| 42 | [ATTRIBUTE] |
| 43 | [DOCSTRING] |
| 44 | SETBLOCK |
| 45 | }}} |
| 46 | |
| 47 | 1. declare a property name mapping onto an '''existing''' declared (possibly private) backing variable named similarly to the property name.[[BR]] |
| 48 | Either the backing is named the same as the property name or has a leading single or double underscore prefix [[BR]] |
| 49 | The properties type is the same as the backing variable, the access modifiers are defaulted or optionally specified. |
| 50 | 2. declare a property name mapping onto the '''existing''' declared named backing variable (VARNAME).[[BR]] |
| 51 | The properties type is the same as the backing variable, the access modifiers are defaulted or optionally specified. |
| 52 | 3. declare a property name mapping onto an '''implicitly''' created private backing variable.[[BR]] |
| 53 | Either the Type or an initialising expression must be given to type the backing variable,[[BR]] |
| 54 | Access modifier may be explicitly specified or defaults. |
| 55 | 4. Full Property form: [[BR]] |
| 56 | must specify property type unless want type dynamic, optionally specify access modifiers and get and set mediating code.[[BR]] |
| 57 | If property is declared abstract or is inside an interface then no body (get set blocks ) needed[[BR]] |
| 58 | otherwise there must be one or both of the set-block or get-block clauses[[BR]] |
| 59 | If no Type specified then property is of type '''dynamic'''[[BR]] |
| 60 | In a SETBLOCK, any assigned value is accessed through a synthesized variable 'value'. |
| 61 | |
| 62 | |
| 63 | For all the above |
| 64 | "is ACCESSMODIFIER" can be on the same line or the next. (see AccessModifiers )[[BR]] |
| 65 | wiki:Attributes and a wiki:Docstring can be optionally provided ( indented on following lines). |
| 66 | |
| 67 | |
| 68 | A (single) indexed property can also be specified by providing a paramlist enclosed in square brackets for the property name. |
| 77 | |
| 78 | == Examples == |
| 79 | |
| 80 | {{{ |
| 81 | #!cobra |
| 82 | # The vars are already declared so use "... from var": |
| 83 | |
| 84 | class Person1 |
| 85 | var _name as String |
| 86 | var _age as int |
| 87 | |
| 88 | cue init(name as String) |
| 89 | base.init |
| 90 | _name = name |
| 91 | |
| 92 | pro name from var |
| 93 | """ |
| 94 | The "from var" indicates that the type of the property, as well |
| 95 | as its value, is taken from the class variable with the matching |
| 96 | name (underscore + property name or underscore + underscore + property name). |
| 97 | When you later want to add code for this property, just chop off the "from var" and write |
| 98 | the full property (pro foo as Type; get ...; set ...) |
| 99 | """ |
| 100 | |
| 101 | pro age from var |
| 102 | |
| 103 | |
| 104 | # If the underlying variable name is different than the property name, |
| 105 | # you can specify that name instead of "var": |
| 106 | |
| 107 | class Person2 |
| 108 | |
| 109 | var _name as String |
| 110 | |
| 111 | cue init(name as String) |
| 112 | base.init |
| 113 | _name = name |
| 114 | |
| 115 | pro nombre from _name |
| 116 | |
| 117 | |
| 118 | |
| 119 | # properties and their backing vars are declared in one line: |
| 120 | class Person3 |
| 121 | |
| 122 | cue init(name as String) |
| 123 | base.init |
| 124 | _name = name |
| 125 | |
| 126 | pro name from var as String |
| 127 | pro age from var as int |
| 128 | |
| 129 | |
| 130 | # Give an initial value to the declaration to infer the data type. |
| 131 | class Person3 |
| 132 | |
| 133 | pro name from var = '' # inferred as String |
| 134 | pro age from var = 0 # inferred as int |
| 135 | |
| 136 | |
| 137 | # Full property form mapping to backing variable |
| 138 | class Person4 |
| 139 | var _name as String |
| 140 | |
| 141 | pro name as String |
| 142 | """ |
| 143 | The name of the person. Setting the name trims preceding and |
| 144 | trailing whitespace from the value. |
| 145 | """ |
| 146 | get |
| 147 | return _name |
| 148 | set |
| 149 | # `value` is an implicit argument in all property setters: |
| 150 | _name = value.trim |
| 151 | |
| 152 | |
| 153 | |
| 154 | # Properties can be read-only: |
| 155 | class Person6 |
| 156 | |
| 157 | cue init(name as String) |
| 158 | base.init |
| 159 | _name = name |
| 160 | |
| 161 | get name from var as String |
| 162 | |
| 163 | get lowerName as String |
| 164 | return _name.toLower |
| 165 | |
| 166 | |
| 167 | # Properties can be write-only: |
| 168 | class Person7 |
| 169 | |
| 170 | cue init(name as String) |
| 171 | base.init |
| 172 | _name = name |
| 173 | |
| 174 | pro name from var as String |
| 175 | |
| 176 | set internalName from var as String |
| 177 | |
| 178 | def toString as String is override |
| 179 | return 'Person7(name=[.name]. internal=[_internalName])' |
| 180 | |
| 181 | |
| 182 | #indexer |
| 183 | class Test |
| 184 | |
| 185 | pro [i as int] as int |
| 186 | get |
| 187 | return i*i |
| 188 | set |
| 189 | pass |
| 190 | |
| 191 | def main is shared |
| 192 | Test().check |
| 193 | t = Test() |
| 194 | assert t[3] == 9 |
| 195 | |
| 196 | |
| 197 | def check |
| 198 | assert this[1]==1 |
| 199 | assert this[2]==4 |
| 200 | assert this[-3]==9 |
| 201 | this[1] = 5 |
| 202 | this[2] = 6 |
| 203 | assert this[1]==1 |
| 204 | assert this[2]==4 |
| 205 | }}} |
| 206 | |
| 207 | |