Try OpenEdge Now
skip to main content
Object-oriented Programming
Programming with Class-based Objects : Instantiating and managing class-based objects : Passing object reference parameters
 

Passing object reference parameters

When an application passes an object reference to a method, user-defined function, or procedure, the assignment rules can be summarized by designating the object that provides the reference as the source and the object that receives the reference as the target. The target object reference definition can be:
*The same type as the source (for example, to acme.myObjs.CustObj from acme.myObjs.CustObj)
*A super class of the source (for example, to acme.myObjs.Common.CommonObj from acme.myObjs.CustObj)
*An interface that the source implements (for example, to acme.myObjs.Interfaces.IBusObj from acme.myObjs.CustObj)
For more information on the rules for assigning object references, see Assigningobject references.
To pass an object between an AppServer and an ABL client, the class of the object must be defined on both the sending and receiving side, and the definitions of the class’s data members, properties, events, and method signatures must match exactly. The class is serialized by the sender, and the receiver uses that information to create a copy of the object.
The following restrictions apply to objects being passed as parameters (or thrown as errors) between an AppServer and client:
*In the case of a user-defined class, the object’s class and all of the classes in its hierarchy must be marked as SERIALIZABLE. For more information on marking a class as SERIALIZABLE, see Usingthe CLASS construct.
*The content of static data members is not serialized, and the state of queries, buffers, open files, streams, and event subscriptions, for example, are not maintained.
*All of the object's data members that are defined as class-based objects must be of a class type that is also marked SERIALIZABLE. (This restriction does not apply to static data members that are defined as objects, because static data members are not serialized.)
*Handle-based variables are serialized, but no information for reconstructing handle-based objects on the receiving side is serialized.
*MEMPTRs assigned by an ABL application are serialized, but MEMPTRs from an external source (such as a DLL or shared library) are not serialized.
*Statically defined temp-tables and ProDataSets in user-defined classes are serialized, except for REFERENCE-ONLY tables.
*The REJECTED, ERROR, ERROR-STRING, and DATA-SOURCE-MODIFIED attributes for temp-tables in an object are maintained as part of the deserialization process. Similarly, the REJECTED and ERROR attributes for ProDataSets are maintained during serialization.
*Not all built-in classes are serializable. See the CLASS statement entry in OpenEdge Development: ABL Reference for a full list of serializable built-in classes.
*.NET and ABL-extended .NET objects cannot be passed as parameters.
For more information on passing objects between an AppServer and an ABL client, see OpenEdge Application Server: Developing AppServer Applications.
Following are a series of examples that demonstrate the passing of object reference parameters for INPUT, INPUT-OUTPUT, and OUTPUT. These examples highlight code from the Main class shown previously (see Defining an object reference variable or property) and related sample classes that are described and fully listed in Sample classes. Refer to these samples for referenced class listings that are not shown in this section.
When passing an INPUT parameter, the caller is the source of the assignment and the invoked method is the target of the assignment. Therefore, the caller must pass an INPUT object reference that is the same class or interface type as the defined method parameter, that is a subclass of the class defined by method parameter, or that is a class which implements the interface defined by the method parameter.
If you pass a super class or interface object reference to the invoked method, the method can only use that object reference to call those methods and access data that are defined by the specified super class or interface, even if the referenced object actually represents a subclass or interface-implementing class that defines additional public methods and data. If the invoked method attempts to use this object reference to call these additional methods or access the additional data without first casting the object reference appropriately, the compiler generates an error. For more information on casting object references, see Object reference assignment and casting.
The following examples pass an INPUT parameter:
USING acme.myObjs.*.
USING acme.myObjs.Common.*.
USING acme.myObjs.Interfaces.*.

CLASS Main:

  DEFINE PRIVATE VARIABLE rCustObj     AS CLASS CustObj     NO-UNDO.
  ...
  DEFINE PRIVATE VARIABLE rHelperClass AS CLASS HelperClass NO-UNDO.
    ...

  METHOD PUBLIC VOID ObjectInfo( ):
    /* Demonstrates passing object references as parameters */
    /* INPUT: It is valid to pass a subclass to a method defined to take a
       super class */
    rHelperClass:InitializeDate (rCustObj).    ...
  END METHOD.

  ...

END CLASS.
USING acme.myObjs.*.
USING acme.myObjs.Common.*.
USING acme.myObjs.Interfaces.*.

CLASS acme.myObjs.Common.HelperClass:
  ...
  DEFINE PRIVATE VARIABLE rMsg AS CLASS MsgObj NO-UNDO.
  ...

  METHOD PUBLIC VOID InitializeDate (INPUT prObject AS CLASS CommonObj):
    /* Timestamp this object */
    IF VALID-OBJECT(prObject) THEN
      prObject:updateTimestamp ( ).    ELSE
     rMsg:Alert ("Not a valid object").
  END METHOD.

  ...

END CLASS.
The previous example shows an acme.myObjs.CustObj instance passed as input to the InitializeDate( ) method, which is defined to take an acme.myObjs.Common.CommonObj (super class of CustObj). Note that the updateTimestamp( ) method invoked on the input object reference only exists in CommonObj. (For a listing of this class, see Calling methods from inside a class hierarchy where theyare defined.) This method can be invoked on any object that is a subclass of CommonObj. For more information on acme.myObjs.Common.CommonObj, see Sample classes.
When you pass an object reference data element as an INPUT-OUTPUT parameter, it must have the same class or interface type as the corresponding parameter definition. Because the object is being passed in both directions, its type must match the parameter definition exactly.
The following examples pass an INPUT-OUTPUT parameter:
USING acme.myObjs.*.
USING acme.myObjs.Common.*.
USING acme.myObjs.Interfaces.*.

CLASS Main:

  DEFINE PRIVATE VARIABLE rCustObj     AS CLASS CustObj     NO-UNDO.
  DEFINE PRIVATE VARIABLE rHelperClass AS CLASS HelperClass NO-UNDO.
  ...

  METHOD PUBLIC VOID ObjectInfo( ):
    /* Demonstrates passing object references as parameters */
    ...

    /* INPUT-OUTPUT: Must be an exact match, a class to a method defined to
       take that same class type */
    rHelperClass:ListNames(INPUT-OUTPUT rCustObj).    rCustObj:CheckCredit( ).    ...
  END METHOD.

  ...

END CLASS.
USING acme.myObjs.*.
USING acme.myObjs.Common.*.
USING acme.myObjs.Interfaces.*.

CLASS acme.myObjs.Common.HelperClass:

DEFINE PRIVATE VARIABLE rCustObj AS CLASS CustObj NO-UNDO.
  ...

  METHOD PUBLIC VOID ListNames (INPUT-OUTPUT prCustObj AS CLASS CustObj):
    DEFINE VARIABLE idx AS INTEGER NO-UNDO.

    DO idx = 1 to prCustObj:iNumCusts:
      CREATE ttNames.
      ttNames.CustName = prCustObj:GetCustomerName (idx).
    END.
    rCustObj = prCustObj.
  END METHOD.

END CLASS.
The previous example shows an acme.myObjs.CustObj instance passed as an INPUT-OUTPUT parameter to the ListNames( ) method in acme.myObjs.Common.HelperClass. In this case, the type on both sides (the passed object reference and the parameter definition) have to be exactly the same, in this case CustObj. The ListNames( ) method uses the input object reference and stores the value in its PRIVATE data member, rCustObj.
When passing an OUTPUT parameter, the caller is the target of the assignment and the invoked method is the source of the assignment. Therefore, the caller must pass a parameter that is the same class or interface type as the defined method parameter, that is a super class of the class defined by method parameter, or that is an interface which is implemented by the class defined by the method parameter.
Once again, if the caller passes a super class or interface object reference data element for OUTPUT, the caller can only use that object reference to invoke those methods and access data that are defined by the specified super class or interface, even if the referenced object actually represents a subclass or interface-implementing class that defines additional public methods and data. If the caller attempts to use this object reference to call these additional methods or access the additional data without first casting the object reference appropriately, the compiler generates an error. For more information on casting object references, see Usingthe CAST function.
The following examples pass an OUTPUT parameter:
USING acme.myObjs.*.
USING acme.myObjs.Common.*.
USING acme.myObjs.Interfaces.*.

CLASS Main:

  DEFINE PRIVATE VARIABLE cOutFile     AS CHARACTER         NO-UNDO.
  DEFINE PRIVATE VARIABLE rHelperClass AS CLASS HelperClass NO-UNDO.
DEFINE PRIVATE VARIABLE rIBusObj     AS CLASS IBusObj     NO-UNDO.
  ...

  METHOD PUBLIC VOID ObjectInfo( ):
    /* Demonstrates passing object references as parameters */
    ...

    /* OUTPUT: An interface is used to receive a class that implements that
       interface */
    rHelperClass:ReportOutput (OUTPUT rIBusObj).
    IF piInfoCount <> ? AND piInfoCount > 1 THEN
      rIBusObj:printObj(piInfoCount).
    ELSE
      rIBusObj:printObj( ).
rIBusObj:logObj (outFile).
    ...
  END METHOD.
  ...
END CLASS.
USING acme.myObjs.*.
USING acme.myObjs.Common.*.
USING acme.myObjs.Interfaces.*.

CLASS acme.myObjs.Common.HelperClass:
DEFINE PRIVATE VARIABLE rCustObj AS CLASS CustObj NO-UNDO.
  DEFINE PRIVATE VARIABLE rMsg     AS CLASS MsgObj  NO-UNDO.
  ...

  METHOD PUBLIC VOID ReportOutput (OUTPUT prInterface AS CLASS IBusObj):
    /* Send the PRIVATE CustObj instance back to be printed */
    IF VALID-OBJECT(rCustObj) THEN
      prInterface = rCustObj.
    ELSE
      rMsg:Alert("The object is not valid").
  END METHOD.
END CLASS.
In the previous example, ReportOutput( ) defines an OUTPUT parameter defined as an object reference to the acme.myObjs.Interfaces.IBusObj interface. Through the OUTPUT parameter, this method in acme.myObjs.Common.HelperClass returns a PRIVATE object reference to an acme.myObjs.CustObj, which is initially set to a CustObj instance by the ListNames( ) method of the class. This works because the CustObj class implements the IBusObj interface. Therefore, ReportOutput( ) can return any object that implements IBusObj. Note that after ReportOutput( ) returns in ObjectInfo( ), the logObj( ) method is called on the OUTPUT object reference. This method is declared in the IBusObj interface and implemented in CustObj. It would be invalid to invoke a method on the OUTPUT object reference that is not declared in this interface, even though the original CustObj instance passed as an IBusObj defines additional PUBLIC methods.