Learning
Community
Open Exchange
Global Masters
InterSystems IRIS Data Platform 2019.3 / Application Development / Defining and Using Classes / Defining and Calling Methods
Previous section   Next section

Defining and Calling Methods

This chapter describes the rules and options for creating methods in InterSystems IRIS™ 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. InterSystems IRIS 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:
  • MethodName is the name of the method. For rules, see “Naming Conventions,” earlier in this book.
  • Arguments is a comma-separated list of arguments. For details, see “Specifying Method Arguments.”
  • Classname is an optional class name that represents the type of value (if any) returned by this method. Omit the As Classname part if the method does not return a value.
    The class can be a data type class, an object class, or (less commonly) a class of no type. The class name can be a complete class name or a short class name. For details, see “Package Use When Referring to Classes,” in the chapter “Package Options.”
  • Keywords represents any method keywords. These are optional. See “Compiler Keywords,” earlier in this book. Later sections of this chapter discuss additional keywords.
  • The method implementation depends on the implementation language and type of method; see “Specifying the Implementation Language” and “Types of Methods.” By default, the method implementation consists of zero or more lines of ObjectScript.
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:
  • argname1, argname2, argname3, and so on are the names of the arguments. These names must follow the rules for variable names.
  • type1, type2, type3, and so on are class names. This part of the method definition is intended to describe, to programmers who might use this method, what type of value to pass for the corresponding argument. Generally it is a good idea to explicitly specify the type of each method argument.
    Typically the types are data type classes or object classes.
    The class name can be a complete class name or a short class name. For details, see “Package Use When Referring to Classes,” in the chapter “Defining and Using Packages.”
    You can omit this part of the syntax. If you do, also omit the as part.
  • value1, value2, value3, and so on are the default values of the arguments. The method automatically sets the argument equal to this value if the method is called without providing a value for the argument.
    Each value can either be a literal value ("abc" or 42) or an ObjectScript expression enclosed in curly braces. For example:
    ClassMethod Test(flag As %Integer = 0)
    {
     //method implementation
    }
    For another example:
    ClassMethod Test(time As %Integer = {$horolog} )
    {
     //method implementation
    }
    You can omit this part of the syntax. If you do, also omit the equals sign (=).
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:
MYNAMESPACE>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 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 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.

Restricting Access by Using Privilege Checks

You can restrict access to a method by using the Requires keyword. Only users or processes that have the specified privilege (or privileges) can call the method.
Specify a privilege by naming a resource and the appropriate level of permission (Use, Read, or Write), separated with a colon. Use a comma-delimited list to specify more than one privilege.
In the following example, the MyAction method requires the Use permission on the Service_FileSystem resource:
ClassMethod MyAction() [ Requires = "Service_FileSystem: Use" ] 
{
  write "You have access to this method."
}
Calling the method without the required privilege results in a <PROTECT> error:
<PROTECT> *Method MyAction' requires resource 'Service_FileSystem: Use' 
If a method inherits the Requires keyword from a superclass, you can add to the list of required privileges by setting a new value for the keyword. You cannot remove required privileges in this manner.
For details, see “Privileges and Permissions” in the Security Administration Guide and “Requires” in the Class Definition Reference.

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 objectscript (ObjectScript). The other option is tsql (TSQL).
You can override this for a specific method by setting the Language keyword for that method:
Class MyApp.Test {

Method TestC() As %Integer [ Language = objectscript ]
{
    // This is ObjectScript
    Write "This is a test"
    Quit 1
}
}

Types of Methods (CodeMode Options)

InterSystems IRIS 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:
InterSystems IRIS 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:
InterSystems IRIS 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 InterSystems IRIS 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 InterSystems IRIS library, method generators are used extensively by the data type and storage classes.

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 InterSystems SQL.

Calling Class Methods

This section discusses how to call class methods in 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.
  • To call a class method of any class (if that method is not private), use the following expression:
    ##class(Package.Class).Method(Args)
    
    Where Package.Class is the name of the class, Method is the name of the method, and Args is any arguments of the method.
    ##class is not case-sensitive.
    This expression invokes the given class method and obtains its return value (if any). You can use this expression with commands such as DO and SET, or you can use it as part of another expression. The following shows some variations:
     do ##class(Package.Class).Method(Args)
     set myval= ##class(Package.Class).Method(Args)
     write ##class(Package.Class).Method(Args) 
     set newval=##class(Package.Class).Method(Args)_##class(Package2.Class2).Method2(Args)
    You can omit the package. If you do so, the class compiler determines the correct package name to use (this name resolution is explained in the “Packages” chapter).
  • (Within a class method) to call another class method of that class (which could be an inherited method), use the following expression:
    ..MethodName(args)
    
    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 ..MethodName()
     set value=..MethodName(args)
    
    Note:
    You cannot use this syntax in a class method to refer to a property or an instance method, because those references require the instance context.
  • To execute a class method, where the method name is not determined until runtime, use the $CLASSMETHOD function:
    $CLASSMETHOD(classname, methodname, Arg1, Arg2, Arg3, ... )
    
    where classname evaluates to the fully qualified name of a class, methodname evaluates to the name of a class method in that class, and Arg1, Arg2, Arg3, and so on are any arguments to that method. For example:
     set cls="Sample.Person"
     set clsmeth="PrintPersons" 
     do $CLASSMETHOD(cls,clsmeth)
    executes the PrintPersons method from the Sample.Person class.
    For more information, see the $CLASSMETHOD page in the ObjectScript Reference.
If the given method does not exist or if it is an instance method instead, the system generates the <METHOD DOES NOT EXIST> error. If the given method is private, the system 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 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 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 the method is a class method in the superclass, you cannot override it as an instance method in the subclass, and vice versa.
  • The return type of the subclass method must be either the same as the original return type or a subclass of the original return type.
  • The method in the subclass can have more arguments than the method in the superclass. (Also see the “Number of Arguments” subsection.)
  • The method in the subclass can specify different default values for the arguments.
  • The types of the arguments in the subclass method must be consistent with the types of the arguments in the original method. Specifically, any given argument must be either the same as the original type or a subclass of the original type.
    Note that if an argument has no specified type, the compiler treats the argument as %String. Thus if an argument in the superclass method has no type, the corresponding argument of a subclass method can be %String, can be a subclass of %String, or can have no type.
  • The method in the subclass should receive argument values in the same way as the method in the superclass. For example, if a given argument is passed by reference in the superclass, the same argument should be passed by reference in the subclass.
    If the method signatures are inconsistent in this regard, it is harder for other developers to know how to use the methods appropriately. Note, however, that the compiler does not issue an error.
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 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.
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:
  • Make sure that the added argument names are not the same as the names of any variables used in the method in the subclass. The compiler appends the added arguments to the method in the subclass. If these arguments happen to have the same names as variables used in the method of the subclass, unintended results will occur.
  • If the method in the subclass uses the added arguments (because this method uses ##super), make sure that the method in the superclass specifies default values for the added arguments.
Previous section   Next section