CLASS class-type-name [ INHERITS super-type-name ]
[ IMPLEMENTS interface-type-name [ , interface-type-name ] ... ]
[ USE-WIDGET-POOL ]
[ ABSTRACT | FINAL ] :
class-body
|
Note that you cannot specify Progress as the first component of the package name for any ABL user-defined class. For example,
Progress.Inventory.UpdateInv is an invalid type name for a user-defined class and results in a compiler error.
The value of class-type-name is restricted to alphanumeric characters plus the symbols #, $, %, and _.
If super-type-name is an abstract class, in this class definition you must implement all the abstract members (properties, methods, and events) that you inherit unless you also define this class as abstract.
IMPLEMENTS interface-type-name [ ,
interface-type-name ] ...
Defines the class as abstract. An abstract class allows you to define any number of instance property, method, or event members, as abstract. Abstract members are prototypes, similar to interface members, without an implementation. Abstract members must be implemented by a derived class to provide data or behavior, but they also function polymorphically in the class hierarchy wherever they are still defined as abstract. So, for example, an abstract property can be accessed or an abstract method can be called and the result depends on its implementation further down in the class hierarchy. Also, whether or not an abstract class defines abstract members, it must be inherited by a derived class. Thus, you cannot directly instantiate an abstract class or define an abstract class as FINAL.
|
Data members — Class members that define instance or static data (or the state) for the class
|
|
Properties — Class members that define instance or static data, similar to data members, but with the option of associated behavior
|
|
Methods — Class members that define instance or static behavior for the class
|
|
Class events — Class members that define instance or static events for the class
|
|
Constructors — Special methods that define initialization for the class, at least one of which is invoked for the class when an instance (object) is created, and a separate one of which is invoked the first time a class with static members is referenced
|
|
Destructor — A special method that defines finalization behavior and that is invoked when the AVM deletes the object using garbage collection or when you delete the object explicitly
|
|
Class-scoped handle-based objects — Handle-based objects that are not class members, but can be defined in and privately scoped to provide certain resources to the class
|
|
Triggers — ON statements, which are not class members, but can be defined in the class to handle events on class-scoped and other handle-based objects
|
[ data-member-definitions ]
[ property-definitions ]
[ method-definitions ]
[ event-definitions ]
[ constructor-definitions ]
[ destructor-definition ]
[ class-scoped-definitions ]
[ trigger-definitions ]
[ udf-prototype-definitions ]
END [ CLASS ].
|
Defines one or more data members of the class. Table 13 lists the types of data members you can define and their associated ABL DEFINE statement. Data members can be defined as instance or static members. An instance data member is scoped to a particular class instance, while a static data member is scoped to the duration of the ABL session.
The following samples include three different sets of class and procedure definitions that provide similar functionality in different ways. Each class defines or inherits a ProDataSet data member (dsHighCustData) and a public class method (
SetHighCustomerData( )) that populates the ProDataSet with selected fields in the
sports2000 database from both a
Customer record and related
Invoice records, where the
Customer has the highest recorded balance in the database and also has related invoices. Each implementation of
SetHighCustomerData( ) also sets two public properties defined by its class (
HighCustBalance and
HighCustNum) to the highest database value for
Customer.Balance and
Customer.CustNum, and publishes a public class event defined by its class for each
Customer record it encounters with related
Invoice records. Each class defines or inherits additional data members to support its own instance of
dsHighCustData, and also defines or inherits a public method (
GetHighCustomerData( )) that returns
dsHighCustData as an output parameter.
1.
|
r-CustObj.cls — Using instance members of an r-CustObj ABL class instance
|
3.
|
r-CustObjAbstractImpl.cls and r-CustObjAbstract.cls — Using instance members of an r-CustObjAbstractImpl ABL class instance, some of which are abstract members inherited from the r-CustObjAbstract ABL abstract class
|
The following r-CustObj.cls sample class file shows many of the basic features of an ABL class definition. It defines only instance members and is defined as FINAL, meaning that it cannot be inherited by a subclass. This class sample also implicitly inherits from
Progress.Lang.Object.
In this class, most of the data members, including the dsHighCustData ProDataSet and its supporting temp-tables, query, and data-sources, are defined as private, allowing access to its data primarily through its public properties and methods. However to demonstrate a public data member, the handle variable for
dsHighCustData is defined as public. The class constructor attaches these data sources to the ProDataSet temp-table buffers and sets the handle variable (
hHighCustData) for reference by the
SetHighCustomerData( ) method to fill and refill the ProDataSet. (An application might need to call this method multiple times to refill the ProDataSet as database
Customer.Balance values change.)
The following r-CustObjProc.p sample procedure file displays data from an
r-CustObj class instance as follows (with numbers corresponding to the numbered comments in the sample):
2.
|
Instantiates the r-CustObj class using the NEW statement, assigning its object reference to a variable ( rObj) that the procedure uses to access this r-CustObj instance and all of its public members
|
5.
|
Calls the SetHighCustomerData( ) method on the r-CustObj instance, which initializes the r-CustObj public properties and private ProDataSet and publishes the CustHasInvoices event, causing the CustHasInvoices_Handler procedure to execute and display a message indicating each Customer that has invoices
|
7.
|
Calls the GetHighCustomerData( ) method on the r-CustObj instance to return its private ProDataSet member, dsHighCustData, as an output parameter, and storing the contents in its own equivalent ProDataSet object
|
8.
|
Displays the contents of dsHighCustData in a down frame, showing fields from the Customer ( ttCust) with the highest stored balance and related Invoice ( ttInv) fields
|
The following r-CustObjStatic.cls sample class file provides functionality similar to
r-CustObj.cls, but using static members instead. In addition, the class is not defined as FINAL. So, it can be inherited by an ABL subclass (not shown, here). This sample class also implicitly inherits from
Progress.Lang.Object. Note that this class could include instance members as well. However, static members cannot reference instance members of the same class hierarchy; so, any instance members would have to support additional functionality, which could only be accessible using an instance of the class. The supported functionality, using static members, is available only as a function of the class type.
So, in this class, all the members are static. Most of the data members, including the dsHighCustData ProDataSet and its supporting temp-tables, buffers, query, and data-sources, are defined as protected, primarily allowing access to its data either by a subclass or through its public properties and methods from outside the class hierarchy. However to demonstrate a public data member, the handle variable for
dsHighCustData is defined as public. The static class constructor (like the constructor in
r-CustObj.cls) attaches these data sources to the ProDataSet temp-table buffers and sets the handle variable (
hHighCustData) for reference by the
SetHighCustomerData( ) to fill and refill the ProDataSet.
As another difference from r-CustObj.cls, note the use of the alternate static buffers (
bHighCust,
bCustomer, and
bInvoice), which allow other static members, such as the static query and method members, to access the database
Customer and
Invoice tables. The default buffers of database tables cannot be accessed from a static class member because ABL treats these buffers implicitly as instance members of the same class.
The following r-CustObjStaticProc.p sample procedure file displays static data from the
r-CustObjStatic class type in a manner similar to how
r-CustObjProc.p displays instance data from an
r-CustObj class instance, except all references to public members are through the class type (
r-CustObjStatic) instead of through an object reference (
rObj in
r-CustObjProc.p). Thus,
r-CustObjStaticProc.p has no need to instantiate
r-CustObjStatic. Otherwise, the application is exactly the same.
The following sample class files, r-CustObjAbstract.cls and
r-CustObjAbstractImpl.cls, provide functionality similar to
r-CustObj.cls, but using a combination of abstract and non-abstract instance members that are defined in the abstract class,
r-CustObjAbstract, and inherited by the non-abstract class,
r-CustObjAbstractImpl.
The r-CustObjAbstract.cls class file defines the
r-CustObjAbstract abstract class. Its abstract members consist of most of the public members of the class, including properties, methods, and the class event. This class also implicitly inherits from
Progress.Lang.Object.
In this class, all the data members (which are always non-abstract), including the dsHighCustData ProDataSet and its supporting handle variable, temp-tables, buffers, query, and data-sources, are defined as protected, allowing direct access to its data from any subclass that inherits from it or through its public properties and methods (once implemented) from outside the class hierarchy. The abstract class constructor (like the constructor in
r-CustObj.cls) attaches the data sources to the ProDataSet temp-table buffers and sets the handle variable (
hHighCustData) for reference by the subclass that implements the abstract
SetHighCustomerData( ) method to fill and refill the ProDataSet. This constructor also subscribes a private method (
CustHasInvoices_Handler( )) as a handler for the abstract
CustHasInvoices event. This handler thus always responds to the event, no matter how it is implemented. Note that the class cannot publish the event, because it has not yet been implemented.
One public method, GetHighCustomerData( ), is implemented (not abstract) because its only function is to return
dsHighCustData as an output parameter. If necessary, an abstract subclass can still override it again as abstract for implementation further down the class hierarchy, or it can simply be overridden by any subclass. The abstract method,
SetHighCustomerData( ), is intended to be implemented any number of ways. For example, an alternative implementation could accumulate an actual
Customer balance from related
Invoice.Amount values instead of using the stored value of
Customer.Balance, which does not necessarily match this total. For an example of this implementation, see the
r-ICustObjectImpl2.cls sample class file, which is described in the Examples section of the
INTERFACE statement reference entry.
Also, note the use of the protected alternate buffers (bCustomer and
bInvoice). These buffers allow access to the database
Customer and
Invoice tables by certain protected class member definitions, including the ProDataSet query and data-sources, that cannot reference the default buffers of database tables. ABL implicitly treats the default buffers of database tables as private instance members of any class that references them; so they cannot be inherited along with any protected member definitions where they might be included. As such, the protected buffers allow any derived class to access the same buffers that are referenced in the protected member definitions it inherits.
The r-CustObjAbstractImpl.cls class file defines the non-abstract
r-CustObjAbstractImpl class, which inherits
r-CustObjAbstract and implements its abstract members. The class is not defined as FINAL. So, it can be inherited by an ABL subclass (not shown, here).
The abstract member implementations include initial values for the HighCustBalance and
HighCustNum properties, the
CustHasInvoices event so it can be published, and the
SetHighCustomerData( ) method, which is implemented almost exactly the same as for
r-CustObj, to set these properties, fill the ProDataSet, and publish the event when appropriate.
Note that from within r-CustObjAbstractImpl, members inherited from
r-CustObjAbstract (such as
hHighCustData and
HighCustBalance) are accessed without any prefix, as if they were defined in the same class. You can access any inherited member of a super class this way. However, if an inherited instance member has the name of a reserved keyword, you must prefix the member reference with THIS-OBJECT. For more information, see the reference entry for the
THIS-OBJECT system reference. For an inherited static member named with a reserved keyword, you must use static type-name syntax as previously described for the
r-CustObjStatic class.
The following r-CustObjAbstractProc.p sample procedure file displays data from an instance of
r-CustObjAbstractImpl in a manner similar to how
r-CustObjProc.p displays data from an instance of
r-CustObj. The only difference is in the event handler procedure (
CustHasInvoices_Handler) which responds to the
CustHasInvoice event in way that works more smoothly with the event handler provided by the
r-CustObjAbstract class. Otherwise, the application is exactly the same.
|
A class definition (.cls) file can contain only one class definition that is optionally preceded by one or more USING statements, a BLOCK-LEVEL ON ERROR UNDO, THROW statement, or a ROUTINE-LEVEL ON ERROR UNDO, THROW statement. The complete class definition must begin with the CLASS statement and end with the END statement, and the CLASS statement must be the first compilable statement after any USING. BLOCK-LEVEL ON ERROR UNDO, THROW, or ON ERROR UNDO, THROW statements in the file. A class definition file containing a class definition cannot also contain an interface definition.
|
|
In effect, a user-defined class represents a unique data type. In ABL, you can use a class type much as you would any ABL built-in data type. You can define variables, parameters, return types, and class-based properties as a class type. These data elements can then hold a reference to a class instance (object reference). You can also assign an object reference to a temp-table field defined as the Progress.Lang.Object class type; but you cannot assign an object reference to a database table field. You can use the object reference to a class to access PUBLIC instance members of that class. For more information on object references, see the reference entry for a Class-based object reference. You can also use a class type name to access available static members of that class, whether or not an instance of the class exists. For more information on using class type names to access static class members, see the Class-based data member access, Class-based method call, Class-based property access, and Type-name syntax reference entries.
|
|
The class name part of class-type-name can be an ABL reserved keyword (such as Display). If it is a reserved keyword, note that ABL does not fully support user-defined class names that are identical to reserved keywords. For more information, see the Type-name syntax reference entry.
|
|
PUBLIC and PROTECTED data members and properties within a class definition (.cls) file maintain their characteristics throughout the inherited class hierarchy. Thus, you cannot shadow (override) data members and properties in a subclass that are defined in a super class. In other words, you cannot define a data member or property in a subclass using the same name as a PUBLIC or PROTECTED data member or property defined in one of its super classes.
|
|
You can create an instance of a class using the NEW function, the New( ) method of the Progress.Lang.Class class, the NEW statement, or the DYNAMIC-NEW statement, and assign the object reference returned for that instance as the value of a data element defined to reference instances of that class type. You access a class instance, as well as its PUBLIC instance data members, properties, and methods, using its associated object reference. For more information on instantiating classes as objects, see the CONSTRUCTOR statement, NEW function (classes), New( ) method of the Progress.Lang.Class class, NEW statement, or DYNAMIC-NEW statement reference entries. For more information on referencing class instances, see the Class-based object reference entry.
|
|
Although you can override the .NET WndProc( ) method, doing so can be risky. The method will be called for every event, like WM_MOUSEMOVE and WM_PAINT, so it could be called thousands of times for a single form, causing performance issues. It can also be called at unexpected times and cause unexpected behavior. Overriding WndProc when using ABL embedded windows with .NET forms could be especially problematic. See OpenEdge Development: GUI for .NET Programming for more information.
|
Assignment (=) statement,
BLOCK-LEVEL ON ERROR UNDO, THROW statement,
Class-based object reference,
CONSTRUCTOR statement,
DEFINE EVENT statement,
DEFINE PROPERTY statement,
DEFINE VARIABLE statement,
DESTRUCTOR statement,
DYNAMIC-NEW statement,
FUNCTION statement,
INTERFACE statement,
METHOD statement,
NEW function (classes),
New( ) method,
NEW statement,
ON statement,
ROUTINE-LEVEL ON ERROR UNDO, THROW statement, Statements defining other class elements as specified in
Table 13 and
Table 14,
Type-name syntax,
USING statement