Try OpenEdge Now
skip to main content
Object-oriented Programming
Getting Started with Classes, Interfaces, and Objects : Using the CLASS construct : Defining properties within a class : DEFINE PROPERTY statement
 
DEFINE PROPERTY statement
This is the syntax for defining a property using the DEFINE PROPERTY statement:

Syntax

DEFINE [ PRIVATE | PROTECTED | PUBLIC ][ STATIC | ABSTRACT ]
[ OVERRIDE ][ SERIALIZABLE | NON-SERIALIZABLE ] PROPERTY property-name type-spec
[ INITIAL { constant |{[ constant[ , constant]...] }}]
  [ NO-UNDO ]
  accessor-spec
Element descriptions for this syntax diagram follow:
[ PRIVATE | PROTECTED | PUBLIC ]
The access mode for the property. The default property access mode is PUBLIC. Private properties are accessible from within the class where they are defined. An instance can access a private property of another instance if they are both instances of the same class. Protected properties are accessible from within the class where they are defined and in any subclass of the defining class. An instance can access a protected property of a second instance of a class that is at the same level or in a super class in the class hierarchy. Public properties are accessible both within the class hierarchy and from outside the class hierarchy where they are defined. For more information on accessing properties, see Accessing data members and properties.
An abstract property prototype cannot be defined as PRIVATE, and an interface property prototype can only be defined as PUBLIC.
Note: If the property is both readable and writable, a more restrictive access mode can be set for either reading or writing the property (but not both) using the accessor-spec.
[ STATIC ]
Specifies the scope of the property. By default, properties of a class are instance properties, meaning that one copy of a given property is created and scoped to each instance of the class in which it is defined. However, properties can also be defined as static using this keyword. ABL creates only one copy of a static property on first reference to the class type in which it is defined, scopes that property to the class type, and makes it available through that class type for the duration of the ABL session. Unless specified otherwise, any reference to a property in this manual is assumed to be a reference to an instance property.
You cannot define a static property as abstract.
For more information on static properties, see Usingstatic members of a class.
[ ABSTRACT ]
Declares the property as abstract using an abstract property prototype. Each abstract property prototype is declared by a single DEFINE PROPERTY statement, with one or both of the two possible accessors specified, but with no accessor implementations. This means that appropriate access mode and data type information is specified for each accessor, but the accessor code block must be empty. Abstract properties can be defined as either PROTECTED or PUBLIC. These property prototypes cannot specify the INITIAL or STATIC option. An abstract property has no storage allocated for it to hold an initial value and it can only be an instance property.
An abstract property must be implemented in some class (abstract or non-abstract) that derives from the class that defines the abstract property (see the ). The class that implements an abstract property must fully define a corresponding property that matches this property prototype, but without the ABSTRACT option. It must define at least the accessors specified in the prototype, but it can also define any accessor that is not specified in the prototype. In addition the implemented property can have a less restrictive access mode, if available (PUBLIC if the abstract property is PROTECTED).
You can only define an abstract property prototype in an abstract class definition.
[ OVERRIDE ]
Specifies that the property definition overrides an inherited abstract property. This definition can implement the inherited abstract property, which can then provide an initial value for the property and fully define its accessors, or it can redefine the property as abstract as long as the defining class is also abstract. However, note that you cannot override any implemented property that you inherit, because data shadowing is not supported. Also, if the current class definition is abstract, you do not need to override an inherited abstract property that you want to remain abstract, unless you want to specify a less restrictive access mode, or you want to add an unspecified accessor to the inherited abstract prototype.
[ SERIALIZABLE | NON-SERIALIZABLE ]
Class-based objects that are defined as serializable (using the SERIALIZABLE option in the CLASS statement) can be passed as parameters in remotes call between the AppServer and ABL clients and can be serialized to binary or JSON format. By default, both passing an object as a parameter and serializing an object to binary via the Progress.IO.BinarySerializer class include all data members regardless of access mode. However, for JSON serialization via Progress.IO.JsonSerializer, only public data members are serialized. To include a protected or private property during JSON serialization, SERIALIZABLE must be added to the definition. See the Serialize( ) method (JsonSerializer) entry in OpenEdge Development: ABL Reference for more information.
Use the NON-SERIALIZABLE option to exclude a given property from parameter passing between an AppServer and ABL client and from the serialization process via the Progress.IO.BinarySerializer or Progress.IO.JsonSerializer class. A property marked as NON-SERIALIZABLE is assigned its initial value when the class is deserialized.
You can also use the NON-SERIALIZABLE option to make a class serializable that otherwise would not be because one or more of its members are not serializable. For example, if a property itself is a class-based object that is not serializable, adding the NON-SERIALIZABLE keyword to that property will allow the class that contains it to be defined as SERIALIZABLE. See Passingobject reference parameters for full details on what can and cannot be serialized.
Note: Use the SERIALIZABLE option only with properties that are both readable and writable. This option is not for use with properties defined as ABSTRACT or as part of an interface.
property-name
The property name, which identifies either the property or its default memory, depending on the context. This property-name must be unique within the class hierarchy according to its particular class member namespace. A property name must be unique within a namespace that includes the names of properties, variable data members, and class events. For more information, see Namespacesfor naming class members.
Properties defined within a class can be named the same as an ABL reserved keyword. In all contexts, a property whose name is a reserved keyword must be qualified when referenced, either by an object reference (which can be THIS-OBJECT) or if it is static, with a static type name. For more information on accessing properties, see Accessing data members and properties. For more information on using static type-name syntax to reference static properties, see Accessingstatic members.
type-spec
The data type of the property and its default memory, specified using this syntax:
AS {primtive-type-name|[ CLASS ]object-type-name}
   [ EXTENT [constant ]]
Any data type that you specify must be one that is allowed as a return type for a method, including a built-in ABL primitive type (primtive-type-name), or a class or interface type (object-type-name). If you specify a class or interface type, object-type-name can be a fully qualified class name (specifying the package). If you also specify the fully qualified class name or its package with a USING statement, object-type-name can be the unqualified class or interface name. For more information on referencing object type names, see Defining and referencing object type names.
The optional EXTENT [constant] defines the property as an array of data elements, where the element data type is specified by either the AS primitive-type-name option or the AS object-type-name option. This option can specify an array property as either determinate (has a defined number of elements) or indeterminate (has an undefined number of elements). To define a determinate array property, specify the EXTENT option with the constant argument. This optional argument is an integer constant value that represents the number of elements in the property array. To define an indeterminate array property, specify the EXTENT option without the constant argument.
This option participates in defining the data type of the property.
An indeterminate array property can be in one of two states: fixed or unfixed, meaning it either has a fixed dimension or it does not. An indeterminate array property has an unfixed dimension when first defined. You can fix the dimension of an indeterminate array property by:
*Initializing the array values when you define the property using the INITIAL option
*Setting the number of elements in the array property using the EXTENT statement
*Passing a determinate array as a parameter to a procedure, user-defined function, or class-based method whose corresponding parameter is an indeterminate array
Once fixed, ABL treats a fixed indeterminate array as a determinate array.
If you do not use the EXTENT option (or you specify constant as 0), the property is not an array property.
[ INITIAL {constant| [ constant[ , constant] . . . ] }]
This option specifies an initial value for an implemented property, depending on the data type. It allows you to define a single value for a scalar property or all of the element values for an array property, specified as a comma-separated list within the required square brackets ([]).
You cannot specify this option for an abstract or interface property prototype.
[NO-UNDO]
Similar to data members, if a transaction is undone in which the property is accessed, this option retains the latest change to the value of property default memory during the transaction.
accessor-spec
One or two accessors defined for the property that indicate if the property is readable, writable, or both.
This syntax specifies two accessors that allow the property to be both readable and writable:
[ PRIVATE | PROTECTED ]get-accessorset-accessor
  |get-accessor[ PRIVATE | PROTECTED ]set-accessor
The get-accessor represents an accessor that allows the property to be read, and the set-accessor represents an accessor that allows the property to be written. (More detailed syntax for each kind of accessor follows.) The optional access mode applies only to the immediately following accessor and overrides the specified property access mode. The other accessor always inherits the property access mode. If you specify an access mode for one accessor, it must be more restrictive than the property access mode that applies to the other accessor. However, note that if the property is abstract, the accessor access mode can never be PRIVATE, because it must be implemented in, and accessible from, an inheriting class.
So for example, if the property access mode is PROTECTED and you specify a PRIVATE access mode on the set-accessor, the get-accessor assumes the access mode of the property, which is PROTECTED. This specifies that the property can only be written from the defining class or an instance of the defining class. The property can be read only from the defining class, an instance of the defining class, a subclass of the defining class, or an instance of a subclass of the defining class.
If you want the property to be both read and written using the property access mode (the default), do not specify an accessor access mode.
This syntax specifies a single accessor that allows the property to be read:
get-accessor
This accessor allows the property to be read according to the property access mode. If this is the only accessor that you specify, the property is read-only. You cannot override the property access mode for a read-only property.
This is the syntax for a get-accessor, which specifies how the property is read:
GET. | GET ( [array-index-parameter] ) : get-logic END [ GET ].
You can define one of two types of accessors for reading the property:
GET.
A simple GET accessor (defined without an implementation). The property value is read directly from the current value of the default memory for the property. This is the only get-accessor syntax that you can specify for an abstract or interface property prototype.
GET ( [array-index-parameter] ) : get-logic END [ GET ].
A coded GET accessor (defined with an implementation). The property value is read from a value returned from get-logic. This get-logic can include any number and types of statements that are valid for a method. These statements can read and write the value of the default memory specified by property-name or the values of any other accessible data elements. If the property is defined as static, get-logic statements can access only other static members defined within the current class hierarchy in addition to its default memory and local accessor data; instance members defined within the current class hierarchy are inaccessible. However, if the property is defined as an instance member, get-logic statements can access static members as well as other instance members.
If the property is defined as an array using the EXTENT option, the get-logic always works on one element of the array at a time, whether the array is read with or without a subscript. If the array is read without a subscript, get-logic executes once for each element in the property array. If specified, the optional array-index-parameter passes the subscript value to the get-logic for the current element being accessed. This is the syntax:
[ INPUT ]array-index-name AS { INTEGER | INT64 }
The array-index-name provides a name for the index parameter, which can have one of the specified integer data types. Whatever retrieval operation you code for the get-logic can then access the appropriate array element of the property default memory by using array-index-name as the array subscript (property-namearray-index-name ]).
To return a value to the property reader, you must use the RETURN statement. This statement can return any value compatible with the property’s data type, regardless of the value of property-name. If you do not invoke a RETURN statement in get-logic, the property returns the Unknown value (?) to the reader.
Any statement in get-logic that assigns a value to property-name invokes the property SET accessor to assign the value. However, any get-logic statements that read the value of property-name directly read the value most recently assigned to property default memory.
If a statement in get-logic raises an ERROR condition that is not handled using the NO-ERROR option, the condition can be handled by the associated block according to its ON ERROR definition or the presence of an appropriate CATCH statement. If the associated block is the accessor, itself, it can also handle the condition according to the default ON ERROR handling for method and procedure blocks or according to any specified ROUTINE-LEVEL ON ERROR UNDO, THROW statement.
A RETURN ERROR or UNDO, THROW in get-logic that raises ERROR beyond the GET accessor block, raises the ERROR condition on the statement that is reading the property. Any data elements (including property default memory) that have been changed by get-logic retain their latest values or revert to their values before this statement executed, depending on their respective NO-UNDO settings. Any optional RETURN-VALUE setting or error object can be appropriately accessed after control returns from the statement that is reading the property. For more information on handling errors returned from properties, see Raising and handling error conditions.
This syntax specifies a single accessor that allows the property to be writable:
set-accessor
This accessor allows the property to be written according to the specified property access mode. If this is the only accessor that you specify, the property is write-only. You cannot override the property access mode for a write-only property.
This is the syntax for a set-accessor, which specifies how the property is written:
SET. | SET ( parameter-definition[ , array-index-parameter] ) :
  set-logic END [ SET ].
You can define one of two types of accessors for writing the property:
SET.
A simple SET accessor (defined without an implementation). The value written to the property is assigned directly to the default memory for the property. This is the only set-accessor syntax that you can specify for an abstract or interface property prototype.
SET ( parameter-definition[ , array-index-parameter] ) : set-logic END [ SET ].
A coded SET accessor (defined with an implementation). The value written to the property is assigned to the parameter specified by parameter-definition, which has the following syntax:
[ INPUT ]parameter-name AS
  {primtive-type-name|[ CLASS ]object-type-name}
The INPUT parameter (parameter-name) holds the value being written to the property as the property is being set, but does not by itself set the property value. The data type that you specify for the parameter (primtive-type-name or object-type-name) must match the data type you have defined for the property. How the property value is set depends entirely on the statements in set-logic. These statements can include any number and types of statements that are valid for a method. Thus, set-logic statements can access the value being written to the property (parameter-name), and they can read or write the value of property default memory (property-name) or the values of any other data elements that are available to the accessor like any method in the class definition. However, for the property value to be set, at least one statement must assign the value of parameter-name to either property-name or to some other data element maintained by the SET accessor. If the property is defined as static, set-logic statements can access only other static members defined within the current class hierarchy in addition to its default memory and local accessor data; instance members defined within the current class hierarchy are inaccessible. However, if the property is defined as an instance member, set-logic statements can access static members as well as other instance members.
If the property is defined as an array using the EXTENT option, the set-logic always works on one element of the array at a time, whether the array is written with or without a subscript. If the array is written without a subscript, set-logic executes once for each element in the property array. If specified, the optional array-index-parameter passes the subscript value to the set-logic for the current element being accessed. This is the syntax:
[ INPUT ]array-index-name AS { INTEGER | INT64 }
The array-index-name provides a name for the index parameter, which can have one of the specified integer data types. Whatever retrieval operation you code for the set-logic can then access the appropriate array element of the property default memory by using array-index-name as the array subscript (property-namearray-index-name ]).
Any statement in set-logic that reads property-name reads the value returned by the property GET accessor. However, any set-logic statements that assign a value to property-name directly assign that value to property default memory.
If a statement in set-logic raises an ERROR condition that is not handled using the NO-ERROR option, the condition can be handled by the associated block according to its ON ERROR definition or the presence of an appropriate CATCH statement. If the associated block is the accessor, itself, it can also handle the condition according to the default ON ERROR handling for method and procedure blocks or according to any specified ROUTINE-LEVEL ON ERROR UNDO, THROW statement.
A RETURN ERROR or UNDO, THROW in >set-logic that raises ERROR beyond the SET accessor block, raises the ERROR condition on the statement that is reading the property. Any data elements (including property default memory) that have been changed by set-logic retain their latest values or revert to their values before this statement executed, depending on their respective NO-UNDO settings. Any optional RETURN-VALUE setting or error object can be appropriately accessed after control returns from the statement that is writing the property. For more information on handling errors returned from properties, see Raising and handling error conditions.
The following example shows a property definition and its access within another sample class definition, acme.myObjs.CreditObj:
ROUTINE-LEVEL ON ERROR UNDO, THROW.

CLASS acme.myObjs.CreditObj:

  DEFINE PUBLIC PROPERTY CustCreditLimit AS DECIMAL INITIAL ? NO-UNDO
    /* GET: Returns the credit limit of the current Customer.       */
    /* If there is no current Customer, it returns Unknown (?).     */
    GET .
    /* SET: Raises the credit limit for Customers in good standing. */
    /* Current increase is $1,000.                                  */
    PROTECTED SET (INPUT piCL AS DECIMAL):
      IF Customer.Balance > piCL THEN DO:
        CustCreditLimit = Customer.Creditlimit.
        UNDO, THROW NEW Progress.Lang.AppError( "Over Limit" ).
      END.
      ELSE
        ASSIGN
          Customer.Creditlimit = piCL + 1000
          CustCreditLimit      = Customer.Creditlimit.
    END SET.
  ...

  METHOD PUBLIC VOID CheckCustCredit ( ):
    /* Invokes the CustCreditLimit property SET accessor */
    IF AVAILABLE Customer THEN
      CustCreditLimit = Customer.Creditlimit.
    ELSE
      UNDO, THROW NEW Progress.Lang.AppError( "No Customer" ).
  END METHOD.

END CLASS.
In this definition, the property CustCreditLimit is publicly readable, but writable only within the definition of CreditObj or any subclass that might be defined. (The CreditObj of a particular instance is also writable from another instance of the same class or subclass.) This property definition uses its default memory (DECIMAL) to store the property value and provides an implementation for only one of its accessors (the SET), which interacts with the Customer buffer currently available in CreditObj (initialization code not shown). Note that the SET accessor throws an error object for an application condition and the public method, CheckCustCredit( ), defined in the same class writes the property value that might trigger the error. Because the property is defined as NO-UNDO, the SET value for the property default memory is retained, even when it raises ERROR in the caller. Any error object is then automatically thrown out of the SET accessor because the class is defined with a ROUTINE-LEVEL ON ERROR UNDO, THROW statement.
For more information on accessing properties, see Accessing data members and properties. For more information on handling property error conditions, see Raisingerrors within a property.