PreviousNextIndex

CLASS statement

Defines a user-defined class. A class defined with this statement represents a user-defined data type whose characteristics are defined by class members, including a set of class data members and properties that define class data, and a set of class methods and events that define class behavior. In addition to class members, a class definition can include special optional methods (one or more constructors and one destructor). It can also include class-scoped handle-based objects that provide private resources to the class, function prototypes for user-defined functions referenced by the class, and triggers to handle events for widgets and other handle-based objects defined by the class.

You can instantiate a non-abstract class as an object using the NEW function (classes), NEW statement, the New( ) method of the Progress.Lang.Class class, or the DYNAMIC-NEW statement. Instantiating a class returns an object reference that provides access to instance members of the class. You can also access static members of a class without instantiating the class.

Note: This statement is applicable only when used in a class definition (.cls) file. For more information, see the Notes section in this reference entry.
Syntax

CLASS class-type-name [ INHERITS super-type-name ] 
  [ IMPLEMENTS interface-type-name [ , interface-type-name ] ... ] 
  [ USE-WIDGET-POOL ] 
  [ ABSTRACT | FINAL ] : 
  class-body 

class-type-name
INHERITS super-type-name
IMPLEMENTS interface-type-name [ , interface-type-name ] ...
USE-WIDGET-POOL
ABSTRACT
FINAL
class-body
data-member-definitions
property-definitions
method-definitions
event-definitions
constructor-definitions
destructor-definition
class-scoped-definitions
trigger-definitions
udf-prototype-definitions
END [ CLASS ]
Examples

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.

Each of the following sample class files or sets of class files implement this functionality:

  1. r-CustObj.cls — Using instance members of an r-CustObj ABL class instance
  2. r-CustObjStatic.cls — Using static members of the r-CustObjStatic ABL class type
  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

Each class or set of classes has an associated sample procedure that accesses the respective class and implements a similar application to display data stored by the 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.)

r-CustObj.cls 
CLASS r-CustObj FINAL: 
  /* Public properties to return basic values for a customer with the 
     highest balance */ 
  DEFINE PUBLIC PROPERTY HighCustBalance AS DECIMAL INITIAL 0.0 NO-UNDO 
    GET. 
    PRIVATE SET. 
  DEFINE PUBLIC PROPERTY HighCustNum AS INTEGER INITIAL ? NO-UNDO 
    GET. 
    PRIVATE SET. 
  /* Public event to notify about Customers with Invoices */ 
  DEFINE PUBLIC EVENT CustHasInvoices 
    SIGNATURE VOID ( piCustNum AS INTEGER ). 
  /* Public handle variable for the high customer ProDataSet */ 
  DEFINE PUBLIC VARIABLE hHighCustData AS HANDLE NO-UNDO. 
  /* Private temp-tables for the high customer ProDataSet */ 
  DEFINE PRIVATE TEMP-TABLE ttCust NO-UNDO LIKE Customer. 
  DEFINE PRIVATE TEMP-TABLE ttInv  NO-UNDO LIKE Invoice. 
  /* Private ProDataSet for a single customer with the highest 
     balance and its invoices */ 
  DEFINE PRIVATE DATASET dsHighCustData FOR ttCust, ttInv 
    DATA-RELATION FOR ttCust, ttInv  
      RELATION-FIELDS (ttCust.CustNum, ttInv.CustNum). 
  /* Private query and data sources for the ProDataSet */ 
  DEFINE PRIVATE QUERY qCust FOR Customer. 
  DEFINE PRIVATE DATA-SOURCE srcCust FOR QUERY qCust. 
  DEFINE PRIVATE DATA-SOURCE srcInv  FOR Invoice. 
  /* Constructor to initialize handles and attach data sources */ 
  CONSTRUCTOR r-CustObj ( ): 
    hHighCustData = DATASET dsHighCustData:HANDLE. 
    BUFFER ttCust:ATTACH-DATA-SOURCE( DATA-SOURCE srcCust:HANDLE ). 
    BUFFER ttInv:ATTACH-DATA-SOURCE( DATA-SOURCE srcInv:HANDLE ). 
  END CONSTRUCTOR. 
  /* Public method to get the current high customer data */ 
  METHOD PUBLIC VOID GetHighCustomerData 
    ( OUTPUT DATASET dsHighCustData BIND ): 
  END METHOD. 
  /* Public method to set (or reset) the current high customer data */ 
  METHOD PUBLIC VOID SetHighCustomerData ( ): 
    hHighCustData:EMPTY-DATASET( ). 
    FOR EACH Customer: /* Find Customer with highest balance */ 
      FIND FIRST Invoice WHERE Invoice.CustNum = Customer.CustNum NO-ERROR. 
      IF AVAILABLE Invoice THEN DO: 
        IF Customer.Balance > HighCustBalance THEN 
          ASSIGN  HighCustBalance = Customer.Balance 
                  HighCustNum     = Customer.CustNum. 
        CustHasInvoices:Publish( Customer.CustNum ). 
      END. 
    END. 
    QUERY qCust:QUERY-PREPARE("FOR EACH Customer "  
      + "WHERE Customer.CustNum = " + STRING(HighCustNum) ). 
    hHighCustData:FILL( ). 
  END METHOD. 
END CLASS. 

To access the instance members of this class from outside its class definition, another class or procedure must instantiate the class and assign its object reference to a property or variable whose data type is the instantiated class type, for example:

DEFINE VARIABLE rObj AS CLASS r-CustObj NO-UNDO. 
rObj = NEW r-CustObj( ) NO-ERROR. 

This example uses the NEW statement to instantiate the class. You can also use the NEW function (classes) or New( ) method in an appropriate expression, or the DYNAMIC-NEW statement. You can then reference the public members of the class using the object reference, for example, calling a public instance method of the class:

rObj:SetHighCustomerData( ) NO-ERROR. 

For more information on using object references, see the reference entry for a Class-based object reference.

To access instance members of a class from within the class definition or a derived class, you can typically reference the member without any prefix, like any local variable of a procedure or method. However, when the class member has the name of a reserved keyword, you must prefix the reference with THIS-OBJECT. For more information, see the reference entry for the THIS-OBJECT system reference.

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):

  1. Defines a static ProDataSet object with a schema that is equivalent to the private dsHighCustData ProDataSet member defined by the r-CustObj class
  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
  3. Subscribes its internal procedure, CustHasInvoices_Handler, as a handler for the public CustHasInvoices event on the r-CustObj instance
  4. Displays a message showing the default values of the r-CustObj public instance properties, HighCustNum and HighCustBalance
  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
  6. Displays a message showing the initialized values of the r-CustObj public instance properties, HighCustNum and HighCustBalance
  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
  9. r-CustObjProc.p
    /* 1 */ 
    DEFINE TEMP-TABLE ttCust NO-UNDO REFERENCE-ONLY LIKE Customer. 
    DEFINE TEMP-TABLE ttInv  NO-UNDO REFERENCE-ONLY LIKE Invoice. 
    DEFINE DATASET dsHighCustData REFERENCE-ONLY FOR ttCust, ttInv 
      DATA-RELATION FOR ttCust, ttInv  
        RELATION-FIELDS (ttCust.CustNum, ttInv.CustNum). 
           
    DEFINE VARIABLE rObj AS CLASS r-CustObj NO-UNDO. 
    /* 2 */ 
    rObj = NEW r-CustObj( ) NO-ERROR. 
    /* 3 */ 
    rObj:CustHasInvoices:Subscribe( "CustHasInvoices_Handler" ) NO-ERROR. 
    /* 4 */ 
    MESSAGE "High Customer Number:" rObj:HighCustNum SKIP  
            "High Customer Balance:" rObj:HighCustBalance VIEW-AS ALERT-BOX. 
    /* 5 */ 
    rObj:SetHighCustomerData( ) NO-ERROR. 
    /* 6 */ 
    MESSAGE "High Customer Number:" rObj:HighCustNum SKIP 
            "High Customer Balance:" rObj:HighCustBalance VIEW-AS ALERT-BOX. 
    /* 7 */ 
    rObj:GetHighCustomerData( OUTPUT DATASET dsHighCustData BIND ) NO-ERROR. 
    /* 8 */ 
    CURRENT-WINDOW:WIDTH-CHARS = 90. 
    FOR EACH ttCust, EACH ttInv BREAK BY ttInv.CustNum:  
      DISPLAY ttCust.CustNum WHEN FIRST-OF(ttInv.CustNum) 
              ttCust.Name WHEN FIRST-OF(ttInv.CustNum) 
              ttCust.Balance WHEN FIRST-OF(ttInv.CustNum)  
              ttInv.InvoiceNum ttInv.Amount SKIP  
        WITH FRAME A WIDTH 90 DOWN  
             TITLE "Customer with highest stored balance" NO-ERROR. 
    END. 
    PROCEDURE CustHasInvoices_Handler: 
      DEFINE INPUT PARAMETER pCustNum AS INTEGER. 
         
      FIND FIRST Customer WHERE Customer.CustNum = pCustNum NO-ERROR. 
      IF AVAILABLE Customer THEN  
        MESSAGE "Customer" Customer.CustNum ('"' + Customer.Name + '"')  
                "has a stored balance of" Customer.Balance  
                "and also has Invoices."  
                VIEW-AS ALERT-BOX. 
    END PROCEDURE. 
    

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.

r-CustObjStatic.cls
CLASS r-CustObjStatic: 
  /* Public static properties to return basic values for a customer with the 
     highest balance */ 
  DEFINE PUBLIC STATIC PROPERTY HighCustBalance AS DECIMAL INITIAL 0.0 NO-UNDO 
    GET. 
    PROTECTED SET. 
  DEFINE PUBLIC STATIC PROPERTY HighCustNum AS INTEGER INITIAL ? NO-UNDO 
    GET. 
    PROTECTED SET. 
/* Public static event to notify about Customers with Invoices */ 
  DEFINE PUBLIC STATIC EVENT CustHasInvoices  
    SIGNATURE VOID ( piCustNum AS INTEGER ). 
  /* Public static handle variable for the high customer ProDataSet */ 
  DEFINE PUBLIC STATIC VARIABLE hHighCustData AS HANDLE NO-UNDO. 
  /* Protected static temp-tables for the high customer ProDataSet */ 
  DEFINE PROTECTED STATIC TEMP-TABLE ttCust NO-UNDO LIKE Customer. 
  DEFINE PROTECTED STATIC TEMP-TABLE ttInv  NO-UNDO LIKE Invoice. 
  /* Protected static ProDataSet for a single customer with the highest 
     balance and its invoices */ 
  DEFINE PROTECTED STATIC DATASET dsHighCustData FOR ttCust, ttInv 
    DATA-RELATION FOR ttCust, ttInv  
      RELATION-FIELDS (ttCust.CustNum, ttInv.CustNum). 
  /* Protected static buffers for the ProDataSet query and data sources */ 
  DEFINE PROTECTED STATIC BUFFER bHighCust FOR Customer. 
  DEFINE PROTECTED STATIC BUFFER bCustomer FOR Customer. 
  DEFINE PROTECTED STATIC BUFFER bInvoice  FOR Invoice. 
  /* Protected static query and data sources for the ProDataSet */ 
  DEFINE PROTECTED STATIC QUERY qCust FOR bCustomer. 
  DEFINE PROTECTED STATIC DATA-SOURCE srcCust FOR QUERY qCust. 
  DEFINE PROTECTED STATIC DATA-SOURCE srcInv  FOR bInvoice. 
  /* Static constructor */ 
  CONSTRUCTOR STATIC r-CustObjStatic ( ): 
    hHighCustData = DATASET dsHighCustData:HANDLE. 
    BUFFER ttCust:ATTACH-DATA-SOURCE( DATA-SOURCE srcCust:HANDLE ). 
    BUFFER ttInv:ATTACH-DATA-SOURCE( DATA-SOURCE srcInv:HANDLE ). 
  END CONSTRUCTOR. 
  /* Public static method to get the current high customer data */ 
  METHOD PUBLIC STATIC VOID GetHighCustomerData 
    ( OUTPUT DATASET dsHighCustData BIND ): 
  END METHOD. 
  /* Public static method to set (or reset) the current high customer data */ 
  METHOD PUBLIC STATIC VOID SetHighCustomerData ( ): 
    hHighCustData:EMPTY-DATASET( ). 
    FOR EACH bHighCust: /* Find first customer with highest balance */ 
      FIND bInvoice WHERE bInvoice.CustNum = bHighCust.CustNum NO-ERROR. 
      IF AVAILABLE bInvoice THEN DO: 
        IF bHighCust.Balance > HighCustBalance THEN 
          ASSIGN  HighCustBalance = bHighCust.Balance 
                  HighCustNum     = bHighCust.CustNum. 
        CustHasInvoices:Publish( bHighCust.CustNum ). 
      END. 
    END. 
    QUERY qCust:QUERY-PREPARE("FOR EACH bCustomer "  
      + "WHERE bCustomer.CustNum = " + STRING(HighCustNum) ). 
    hHighCustData:FILL( ). 
  END METHOD. 
END CLASS. 

To access the static members of this class from outside of its class definition (and any derived class), you do not need to instantiate the class. You only need to reference the member using static type-name syntax, for example:

r-CustObjStatic:SetHighCustomerData( ) NO-ERROR. 

For more information on static type-name syntax, see the Type-name syntax, Class-based data member access, Class-based method call, and Class-based property access reference entries.

To access static members of a class from within the class definition or a derived class, you can typically reference the member without any prefix, like any local variable of a procedure or method. However, when the class member has the name of a reserved keyword, you must prefix the reference using static type-name syntax.

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.

r-CustObjStaticProc.p
DEFINE TEMP-TABLE ttCust NO-UNDO REFERENCE-ONLY LIKE Customer. 
DEFINE TEMP-TABLE ttInv  NO-UNDO REFERENCE-ONLY LIKE Invoice. 
DEFINE DATASET dsHighCustData REFERENCE-ONLY FOR ttCust, ttInv 
  DATA-RELATION FOR ttCust, ttInv  
      RELATION-FIELDS (ttCust.CustNum, ttInv.CustNum). 
r-CustObjStatic:CustHasInvoices:Subscribe( "CustHasInvoices_Handler" ) 
  NO-ERROR. 
MESSAGE "High Customer Number:" r-CustObjStatic:HighCustNum SKIP  
        "High Customer Balance:" r-CustObjStatic:HighCustBalance VIEW-AS 
ALERT-BOX. 
         
r-CustObjStatic:SetHighCustomerData( ) NO-ERROR. 
MESSAGE "High Customer Number:" r-CustObjStatic:HighCustNum SKIP  
        "High Customer Balance:" r-CustObjStatic:HighCustBalance VIEW-AS 
ALERT-BOX. 
r-CustObjStatic:GetHighCustomerData( OUTPUT DATASET dsHighCustData BIND ) 
  NO-ERROR. 
CURRENT-WINDOW:WIDTH-CHARS = 90. 
FOR EACH ttCust, EACH ttInv BREAK BY ttInv.CustNum:  
  DISPLAY ttCust.CustNum WHEN FIRST-OF(ttInv.CustNum) 
          ttCust.Name WHEN FIRST-OF(ttInv.CustNum) 
          ttCust.Balance WHEN FIRST-OF(ttInv.CustNum)  
          ttInv.InvoiceNum ttInv.Amount SKIP  
    WITH FRAME A WIDTH 90 DOWN  
         TITLE "Customer with highest stored balance" NO-ERROR. 
END. 
PROCEDURE CustHasInvoices_Handler: 
  DEFINE INPUT PARAMETER pCustNum AS INTEGER. 
     
  FIND FIRST Customer WHERE Customer.CustNum = pCustNum NO-ERROR. 
  IF AVAILABLE Customer THEN  
    MESSAGE "Customer" Customer.CustNum ('"' + Customer.Name + '"')   
            "has a stored balance of" Customer.Balance  
            "and also has Invoices."  
            VIEW-AS ALERT-BOX. 
END PROCEDURE. 

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.

r-CustObjAbstract.cls 
CLASS r-CustObjAbstract ABSTRACT: 
   
  /* Public abstract properties to return basic values for a customer  
     with the highest balance */ 
  DEFINE PUBLIC ABSTRACT PROPERTY HighCustBalance AS DECIMAL NO-UNDO 
    GET. 
    PROTECTED SET. 
  DEFINE PUBLIC ABSTRACT PROPERTY HighCustNum AS INTEGER NO-UNDO 
    GET. 
    PROTECTED SET. 
     
  /* Public abstract event to notify about Customers with Invoices */ 
  DEFINE PUBLIC ABSTRACT EVENT CustHasInvoices  
    SIGNATURE VOID ( piCustNum AS INTEGER ). 
  /* Protected handle variable for the high customer ProDataSet */ 
  DEFINE PROTECTED VARIABLE hHighCustData AS HANDLE NO-UNDO. 
  /* Protected temp-tables for the high customer ProDataSet */ 
  DEFINE PROTECTED TEMP-TABLE ttCust NO-UNDO LIKE Customer. 
  DEFINE PROTECTED TEMP-TABLE ttInv  NO-UNDO LIKE Invoice. 
  /* Protected ProDataSet for a single customer with the highest 
     balance and its invoices */ 
  DEFINE PROTECTED DATASET dsHighCustData FOR ttCust, ttInv 
    DATA-RELATION FOR ttCust, ttInv  
      RELATION-FIELDS (ttCust.CustNum, ttInv.CustNum). 
  /* Protected buffers for the ProDataSet query and data sources */ 
  DEFINE PROTECTED BUFFER bCustomer FOR Customer. 
  DEFINE PROTECTED BUFFER bInvoice  FOR Invoice. 
  /* Private query and data sources for the ProDataSet */ 
  DEFINE PROTECTED QUERY qCust FOR bCustomer. 
  DEFINE PROTECTED DATA-SOURCE srcCust FOR QUERY qCust. 
  DEFINE PROTECTED DATA-SOURCE srcInv  FOR bInvoice. 
 /* Constructor to initialize handles and attach data sources */ 
  CONSTRUCTOR r-CustObjAbstract ( ): 
     
    hHighCustData = DATASET dsHighCustData:HANDLE. 
    BUFFER ttCust:ATTACH-DATA-SOURCE( DATA-SOURCE srcCust:HANDLE ). 
    BUFFER ttInv:ATTACH-DATA-SOURCE( DATA-SOURCE srcInv:HANDLE ). 
    CustHasInvoices:Subscribe( CustHasInvoices_Handler ) NO-ERROR. 
     
  END CONSTRUCTOR. 
  /* Default private event handler for the abstract CustHasInvoices event */ 
  METHOD PRIVATE VOID CustHasInvoices_Handler ( INPUT pArg AS INTEGER ): 
    MESSAGE "Customer" pArg "has Invoices." VIEW-AS ALERT-BOX. 
  END METHOD. 
  /* Public method to get the current high customer data */ 
  METHOD PUBLIC VOID GetHighCustomerData 
    ( OUTPUT DATASET FOR dsHighCustData BIND ): 
       
  END METHOD. 
  /* Public abstract method to set (or reset) the current high customer data */ 
  METHOD PUBLIC ABSTRACT VOID SetHighCustomerData ( ). 
   
END CLASS. 

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.

r-CustObjAbstractImpl.cls
CLASS r-CustObjAbstractImpl INHERITS r-CustObjAbstract:  
  /* Public properties implemented to return basic values for a customer 
     with the highest balance */ 
  DEFINE OVERRIDE PUBLIC PROPERTY HighCustBalance AS DECIMAL INITIAL 0.0 
      NO-UNDO 
    GET. 
    PROTECTED SET.   
  DEFINE OVERRIDE PUBLIC PROPERTY HighCustNum AS INTEGER INITIAL ?  
      NO-UNDO 
    GET. 
    PROTECTED SET. 
   
  /* Public event implemented to notify about Customers with Invoices */ 
  DEFINE PUBLIC OVERRIDE EVENT CustHasInvoices 
    SIGNATURE VOID (INPUT piCustNum AS INTEGER). 
  /* Public method implemented to set (or reset) the current high 
     customer data based on the stored value of Customer.Balance */ 
  METHOD OVERRIDE PUBLIC VOID SetHighCustomerData ( ): 
    hHighCustData:EMPTY-DATASET( ). 
    FOR EACH Customer: /* Find Customer with highest balance */ 
      FIND FIRST Invoice WHERE Invoice.CustNum = Customer.CustNum NO-ERROR. 
      IF AVAILABLE Invoice THEN DO: 
        IF Customer.Balance > HighCustBalance THEN 
          ASSIGN  HighCustBalance = Customer.Balance 
                  HighCustNum     = Customer.CustNum. 
        CustHasInvoices:Publish( Customer.CustNum ). 
      END. 
    END. 
    QUERY qCust:QUERY-PREPARE("FOR EACH bCustomer "  
      + "WHERE bCustomer.CustNum = " + STRING(HighCustNum) ). 
    hHighCustData:FILL( ). 
  END METHOD. 
END CLASS. 

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.

r-CustObjAbstractProc.p
DEFINE TEMP-TABLE ttCust NO-UNDO REFERENCE-ONLY LIKE Customer. 
DEFINE TEMP-TABLE ttInv  NO-UNDO REFERENCE-ONLY LIKE Invoice. 
DEFINE DATASET dsHighCustData REFERENCE-ONLY FOR ttCust, ttInv 
  DATA-RELATION FOR ttCust, ttInv  
    RELATION-FIELDS (ttCust.CustNum, ttInv.CustNum). 
       
DEFINE VARIABLE rObj AS CLASS r-CustObjAbstractImpl NO-UNDO. 
rObj = NEW r-CustObjAbstractImpl( ) NO-ERROR. 
rObj:CustHasInvoices:Subscribe( "CustHasInvoices_Handler" ) NO-ERROR. 
MESSAGE "High Customer Number:" rObj:HighCustNum SKIP  
        "High Customer Balance:" rObj:HighCustBalance VIEW-AS ALERT-BOX. 
         
rObj:SetHighCustomerData( ) NO-ERROR. 
MESSAGE "High Customer Number:" rObj:HighCustNum SKIP 
        "High Customer Balance:" rObj:HighCustBalance VIEW-AS ALERT-BOX. 
rObj:GetHighCustomerData( OUTPUT DATASET dsHighCustData BIND ) NO-ERROR. 
CURRENT-WINDOW:WIDTH-CHARS = 90. 
FOR EACH ttCust, EACH ttInv BREAK BY ttInv.CustNum:  
  DISPLAY ttCust.CustNum WHEN FIRST-OF(ttInv.CustNum) 
          ttCust.Name WHEN FIRST-OF(ttInv.CustNum) 
          ttCust.Balance WHEN FIRST-OF(ttInv.CustNum)  
          ttInv.InvoiceNum ttInv.Amount SKIP  
    WITH FRAME A WIDTH 90 DOWN  
         TITLE "Customer with highest stored balance" NO-ERROR. 
END. 
PROCEDURE CustHasInvoices_Handler: 
  DEFINE INPUT PARAMETER pCustNum AS INTEGER. 
     
  FIND FIRST Customer WHERE Customer.CustNum = pCustNum NO-ERROR. 
  IF AVAILABLE Customer THEN  
    MESSAGE pCustNum ('"' + Customer.Name + '"')  
            "has a stored balance of" (STRING(Customer.Balance) + ".")  
            VIEW-AS ALERT-BOX. 
END PROCEDURE. 

For an example of an ABL class that inherits from a .NET class, see the WAIT-FOR statement (.NET and ABL) reference entry.

Notes
See also

Assignment (=) 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 12 and Table 13, Type-name syntax, USING statement


OpenEdge Release 10.2B
Copyright © 2009 Progress Software Corporation
PreviousNextIndex