Using Caché Objects
Defining and Calling Methods
[Back] [Next]
   
Server:docs1
Instance:LATEST
User:UnknownUser
 
-
Go to:
Search:    

This chapter describes the rules and options for creating methods in Caché classes and for calling those methods. It discusses the following topics:

For information on calling instance methods, see the next chapter; such methods apply only to object classes.
When viewing this book online, use the preface of this book to quickly find other topics.
Introduction to Methods
A method is an executable element defined by a class. Caché supports two types of methods: instance methods and class methods. An instance method is invoked from a specific instance of a class and typically performs some action related to that instance. A class method is a method that can be invoked without reference to any object instance; these are called static methods in other languages.
The term method usually refers to an instance method. The more specific phrase class method is used to refer to class methods.
Because you cannot execute an instance method without an instance of an object, instance methods are useful only when defined in object classes. In contrast, you can define class methods in any kind of class.
Defining Methods
To add a class method to a class, add an element like the following to the class definition:
ClassMethod MethodName(Arguments) as Classname [ Keywords]
{
//method implementation
}
Where:
To add an instance method to a class, use the same syntax with Method instead of ClassMethod:
Method MethodName(arguments) as Classname [ Keywords]
{
 //method implementation
}
Instance methods are relevant only in object classes.
Specifying Method Arguments: Basics
A method can take any number of arguments. The method definition must specify the arguments that it takes. It can also specify the type and default value for each argument. (In this context, type refers to any kind of class, not specifically data type classes.)
Consider the following generic class method definition:
ClassMethod MethodName(Arguments) as Classname [ Keywords]
{
 //method implementation
}
Here Arguments has the following general form:
argname1 as type1 = value1, argname2 as type2 = value2, argname3 as type3 = value3, [and so on]
Where:
For instance, here is a Calculate() method with three arguments:
ClassMethod Calculate(count As %Integer, name, state As %String = "CA")
{
 // ...
}
where count and state are declared as %Integer and %String, respectively. By default, arguments are of the %String data type, so that an argument of unspecified type is a %String. This is the case for name in the above example.
Indicating How Arguments Are to Be Passed
The method definition also indicates, to programmers who might use the method, how each argument is expected to be passed. Arguments can be passed by value (the default technique) or by reference. See Passing Arguments to a Method later in this chapter.
It may or may not be sensible to pass a specific argument by reference. The details depend upon the method implementation. Consequently, when you define a method, you should use the method signature to indicate to other programmers how each argument is meant to be used.
To indicate that an argument should be passed by reference, include the ByRef modifier in the method signature, before the name of the argument. The following shows an example that uses ByRef for both its arguments:
/// Swap value of two integers
Method Swap(ByRef x As %Integer, ByRef y As %Integer)
{
    Set temp = x
    Set x = y
    Set y = temp
}
Similarly, to indicate that an argument should be passed by reference and is intended to have no incoming value, include the Output modifier in the method signature, before the name of the argument. For example:
Method CreateObject(Output newobj As MyApp.MyClass) As %Status
{
    Set newobj = ##class(MyApp.MyClass).%New()
    Quit $$$OK
}
Specifying a Variable Number of Arguments
You can define a method that accepts variable numbers of arguments. To do so, include ... after the name of the last argument, as in the following example. This example also shows how this feature can be used.
ClassMethod MultiArg(Arg1... As %String)
{
 Write "Invocation has ",
     $GET(Arg1, 0),
     " element",
     $SELECT(($GET(Arg1, 0)=1):"", 1:"s"),
     !
 For i = 1 : 1 : $GET(Arg1, 0)
 {
     Write:($DATA(Arg1(i))>0) "Argument[", i , "]:", 
         ?15, $GET(Arg1(i), "<NULL>"), !
 }
 Quit
}
The following Terminal session shows how this method behaves:
SAMPLES>do ##class(VarNumArg.Demo).MultiArg("scooby","shaggy","velma","daphne","fred")
Invocation has 5 elements
Argument[1]:   scooby
Argument[2]:   shaggy
Argument[3]:   velma
Argument[4]:   daphne
Argument[5]:   fred
For more details on this feature, see the Variable Numbers of Arguments section of the “User-defined Code” chapter of Using Caché ObjectScript.
Returning a Value
To define a method so that it returns a value, use either of the following in the method (if you implement the method in Caché ObjectScript):
 Return returnvalue
Or:
 Quit returnvalue
Where returnvalue is a suitable value for the method to return. This should be consistent with the declared return type of the method. If the return type is a data type class, the method should return a literal value. If the return type is an object class, the method should return an instance of that class (specifically an OREF). For details, see the chapter Working with Registered Objects.”
For example:
ClassMethod Square(input As %Numeric) As %Numeric
{
    Set returnvalue = input * input
    Return person
}
For another example, this method returns an object instance:
ClassMethod FindPerson(id As %String) As Person
{
    Set person = ##class(Person).%OpenId(id)
    Return person
}
The syntax for returning a value is different depending on the implementation language of the method.
Specifying the Implementation Language
You have the choice of implementation language when creating a method. In fact, within a single class, it is possible to have multiple methods implemented in different languages. All methods interoperate regardless of implementation language.
By default, a method uses the language specified by the Language keyword of the class to which it belongs. For this keyword, the default is cache (Caché ObjectScript). The other options are basic (Caché Basic), java (Java), javascript (JavaScript), mvbasic (MVBasic), and tsql (TSQL).
You can override this for a specific method by setting the Language keyword for that method:
Class MyApp.Test {

/// A Basic method
Method TestB() As %Integer [ Language = basic]
{
    'This is Basic
    Print "This is a test"
    Return 1
}

/// A Cache ObjectScript method
Method TestC() As %Integer [ Language = cache]
{
    // This is Cache ObjectScript
    Write "This is a test"
    Quit 1
}
}
Types of Methods (CodeMode Options)
Caché supports four types of methods, which the class compiler handles differently:
Code Methods
A code method is a method whose implementation is simply lines of code. This is the most typical type of method and is the default.
The method implementation can contain any code that is valid for the implementation language.
Note:
Caché comes with a set of system-defined methods that perform simple, common tasks. If a user-defined method performs one of these tasks, then the compiler does not generate any executable code for it. Rather, it associates the user-defined method with the system-defined method, so that invoking the user-defined method results in a call to the system-defined method, with an associated performance benefit. Also, the debugger does not step through such a system-defined method.
Expression Methods
An expression method is a method that may be replaced by the class compiler, in certain circumstances, with a direct in-line substitution of a specified expression. Expression methods are typically used for simple methods (such as those found in data type classes) that need rapid execution speed.
For example, it is possible to convert the Speak() method of the Dog class from the previous example into an expression method:
Method Speak() As %String [CodeMode = expression]
{
    "Woof, Woof"
}
Assuming dog refers to a Dog object, this method could be used as follows:
 Write dog.Speak()
Which could result in the following code being generated:
 Write "Woof, Woof"
It is a good idea to give default values to all formal variables of an expression method. This prevents potential inline substitution problems caused by missing actual variables at runtime.
Note:
Caché does not allow macros or call-by-reference arguments within expression methods.
Call Methods
A call method is a special mechanism to create method wrappers around existing Caché routines. This is typically useful when migrating legacy code to object-based applications.
Defining a method as a call method indicates that a specified routine is called whenever the method is invoked. The syntax for a call method is:
Method Call() [ CodeMode = call ]
{
    Tag^Routine
}
where “Tag^Routine” specifies a tag name within a routine.
Method Generators
A method generator is actually a small program that is invoked by the class compiler during class compilation. Its output is the actual runtime implementation of the method. Method generators provide a means of inheriting methods that can produce high performance, specialized code that is customized to the needs of the inheriting class or property. Within the Caché library, method generators are used extensively by the data type and storage classes.
For details, see Defining Method and Trigger Generators.”
Projecting a Method As an SQL Stored Procedure
You can define a class method (but not an instance method) so that it is also available as an SQL stored procedure. To do so, include the SqlProc keyword in the method definition.
The default name of the procedure is as CLASSNAME_METHODNAME To specify a different name, specify the SqlName keyword.
For details, see Defining Stored Procedures in Using Caché SQL.
Calling Class Methods
This section discusses how to call class methods in Caché ObjectScript. This section applies to all kinds of classes. Note that instance methods are discussed in the next chapter, because they apply only to object classes.
If the given method does not exist or if it is an instance method instead, Caché generates the <METHOD DOES NOT EXIST> error. If the given method is private, Caché generates the <PRIVATE METHOD> error.
Passing Arguments to a Method
The default way to pass arguments to methods is by value. In this technique, simply include the arguments as variables, literal values, or other expressions within the method call, as shown in the preceding examples.
It is also possible to pass arguments by reference.
This works follows: the system has a memory location that contains the value of each local variable. The name of the variable acts as the address to the memory location. When you pass a local variable to a method, you pass the variable by value . This means that the system makes a copy of the value, so that the original value is not affected. You can pass the memory address instead; this technique is known as calling by reference. It is also the only way to pass a multidimensional array as an argument.
In Caché ObjectScript, to pass an argument by reference, precede that argument with a period. For example:
 set MyArg(1)="value A"
 set MyArg(2)="value B"
 set status=##class(MyPackage.MyClass).MyMethod(.MyArg)
In this example, we pass a value (a multidimensional array) by reference so that the method could receive the value. In other cases, it is useful to pass an argument by reference so that you can use its value after running the method. For example:
 set status=##class(MyPackage.MyClass).GetList(.list)
 //use the list variable in subsequent logic
In other cases, you might specify a value for the variable, invoke a method that modifies it (and that returns it by reference), and then use the changed value.
Casting a Method
To cast a method of one class as a method of another class, the syntax is either of the following (in Caché ObjectScript):
Do ##class(Package.Class1)Class2Instance.Method(Args)
Set localname = ##class(Package.Class1)Class2Instance.Method(Args)
You can cast both class methods and instance methods.
For example, suppose that two classes, MyClass.Up and MyClass.Down, both have Go() methods. For MyClass.Up, this method is as follows
Method Go()
{
    Write "Go up.",!
}
For MyClass.Down, the Go() method is as follows:
Method Go()
{
    Write "Go down.",!
}
You can then create an instance of MyClass.Up and use it to invoke the MyClass.Down.Go method:
>Set LocalInstance = ##class(MyClass.Up).%New()
 
>Do ##class(MyClass.Down)LocalInstance.Go()
Go down.
It is also valid to use ##class as part of an expression, as in
 Write ##class(Class).Method(args)*2
without setting a variable equal to the return value.
A more generic approach is to use the $METHOD and $CLASSMETHOD functions, which are for instance and class methods, respectively. These are described in earlier sections of this chapter.
Overriding an Inherited Method
A class inherits methods (both class and instance methods) from its superclass or superclasses. Except for methods that are marked Final, you can override these definitions by providing a definition within this class. If you do so, note the following rules:
If your method implementation needs to call the method of the same name as defined in the superclass, you can use the syntax ##super(), which is discussed in the subsections. This discussion applies to code that is written in Caché ObjectScript.
##super()
Within a method, use the following expression to call the method of the same name as defined in the nearest superclass:
##super()
You can use this expression with the DO command. If the method returns a value, you can use SET, or you can use it as part of another expression. The following shows some variations:
 do ##super()
 set returnvalue=##super()_"additional string"
Note:
##super is not case-sensitive. Also note that, unlike other features in this chapter, ##super() is available within Basic methods as well as within ObjectScript methods.
This is useful if you define a method that should invoke the existing method of the superclass and then perform some additional steps such as modifying its returned value.
##super and Method Arguments
##super also works with methods that accept arguments. If the subclass method does not specify a default value for an argument, make sure that the method passes the argument by reference to the superclass.
For example, suppose the code for the method in the superclass (MyClass.Up.SelfAdd()) is:
ClassMethod SelfAdd(Arg As %Integer)
{
    Write Arg,!
    Write Arg + Arg
}
then its output is:
>Do ##Class(MyClass.Up).SelfAdd(2)
2
4
>
The method in the subclass (MyClass.Down.SelfAdd()) uses ##super and passes the argument by reference:
ClassMethod SelfAdd(Arg1 As %Integer)
{
    Do ##super(.Arg1)
    Write !
    Write Arg1 + Arg1 + Arg1
}
then its output is:
>Do ##Class(MyClass.Down).SelfAdd(2)
2
4
6
>
In MyClass.Down.SelfAdd(), notice the period before the argument name. If we omitted this and we invoked the method without providing an argument, we would receive an <UNDEFINED> error.
Calls That ##super Affects
##super only affects the current method call. If that method makes any other calls, those calls are relative to the current object or class, not the superclass. For example, suppose that MyClass.Up has MyName() and CallMyName() methods:
Class MyClass.Up Extends %Persistent
{

ClassMethod CallMyName()
{
    Do ..MyName()
}

ClassMethod MyName()
{
    Write "Called from MyClass.Up",!
}

}
and that MyClass.Down overrides those methods as follows:
Class MyClass.Down Extends MyClass.Up
{

ClassMethod CallMyName()
{
    Do ##super()
}

ClassMethod MyName()
{
    Write "Called from MyClass.Down",!
}

}
then invoking the CallMyName() methods have the following results:
USER>d ##class(MyClass.Up).CallMyName()
Called from MyClass.Up
 
USER>d ##class(MyClass.Down).CallMyName()
Called from MyClass.Down
MyClass.Down.CallMyName() has different output from MyClass.Up.CallMyName() because its CallMyName() method includes ##super and so calls the MyClass.Up.CallMyName() method, which then calls the uncast MyClass.Down.MyName() method.
Number of Arguments
In some cases, you might find it necessary to add new arguments to a method in a superclass, thus resulting in more arguments than are defined in the method in a subclass. The subclasses will still compile, because (for convenience) the compiler appends the added arguments to the method in the subclass. In most cases, you should still examine all the subclasses that extend the method, and edit the signatures to account for the additional arguments, and decide whether you want to edit the code also. Even if you do not want to edit signatures or code, you still must consider two points: