Chapter Contents |
Previous |
Next |
SAS Component Language: Reference |
Methods can be declared in CLASS blocks. To declare a method, include the following METHOD statement in your CLASS block:
label : <scope> METHOD <parameter-list></(method-options)>; |
Methods are implemented in METHOD blocks. A METHOD block begins with the METHOD statement, includes the SCL code that implements the method, and then ends with the ENDMETHOD statement.
label : <scope>
METHOD <parameter-list> <OPTIONAL=parameter-list> <ARGLIST=parm-list-id |REST=rest-list-id> RETURN=limited-data-type </ (method-options)>; |
. . .SCL statements that implement the method. . . |
ENDMETHOD; |
For example, the Add method can be implemented in the CLASS block as follows:
class Arithmetic; add: method n1 n2:num; return(n1 + n2); endmethod; endclass;If you want to implement the Add method in a separate SCL entry, then the CLASS block would contain only the method declaration:
class Arithmetic; add: method n1 n2:num / (scl='work.a.b.scl'); endclass;The
work.a.b.scl
entry would
contain a USECLASS block that implements the Add method:
useclass Arithmetic; add: method n1 n2: num; return (n1 + n2); endmethod; enduseclass;
See METHOD for a complete description of implementing methods with the METHOD statement. See The Structure of SCL Programs; Implementing Methods Outside of Classes; and USECLASS for more information about implementing methods in USECLASS blocks.
Note: The method options that you specify in the CLASS block can also
be specified in the USECLASS block. Any option that is included in the CLASS
block and is used to specify a nondefault value must be repeated in the USECLASS
block. For example, if you specify State='O'
or Signature='N'
in the CLASS block, then you
must repeat those options in the USECLASS block. However, the SCL option will
be ignored in the USECLASS block.
For compatibility with Version 6, you can also define METHOD blocks in a separate SCL entry outside of CLASS and USECLASS blocks. However, such an application is not a strictly object-oriented application. For these methods, SCL will not validate method names and parameter types during compile time. See Defining and Using Methods for more information about methods that are not declared or implemented within a class.
Defining Method Scope |
class Scope; m1: public method n:num return=num /(scl='work.a.uScope.scl'); m2: private method :char; /(scl='work.b.uScope.scl'); m3: protected method return=num; num = 3; dcl num n = m1(num); return(n); endmethod; m4: method /(scl='work.c.uScope.scl'); endclass;By default, method m4 is a public method.
Defining Method Names and Labels |
Note: A method that has the same name
as the class that contains it is called a constructor.
See Defining Constructors
for more information.
If you need the method name to be different from the method label, you must specify either the METHOD or LABEL option in the METHOD statement. These options are mutually exclusive.
Note: In dot notation, always use the method name. When implementing
the method, always use the method label.
For example, a label of MyMethod may be sufficient, but if you want the method name to be MySortSalaryDataMethod, you can declare the method as follows:
class a; MyMethod: public method sal:num /(Method='MySortSalaryDataMethod', SCL='work.a.a.scl'); endclass;When you implement the method in
work.a.a.scl
, you identify the method by using the method label as follows:
useclass a; MyMethod: public method sal:num; ...SCL statements... endmethod; enduseclass;You would reference this method in dot notation by using the method name as follows:
obj.MySortSalaryDataMethod(n);
Alternatively, you can specify the LABEL option. For example, to specify a method name of Increase and a method label of CalculatePercentIncrease, you could declare the method as follows:
class a; Increase: public method /(Label='CalculatePercentIncrease', SCL='work.a.b.scl'); endclass;As in the previous example, you use the method label when you implement the method, and you use the method name when you refer to the method in dot notation. In
work.a.b.scl
, you
would implement the method as follows:
useclass a; CalculatePercentIncrease: public method; ...SCL statements... endmethod; enduseclass;You would reference the method in dot notation as follows:
obj.Increase();
In Version 6, SAS/AF software used underscores to separate words in method names (for example, _set_background_color_). The current convention is to use a lowercase letter for the first letter and to subsequently uppercase the first letter of any joined word (for example, _setBackgroundColor).
The embedded underscores have been removed to promote readibility. However, for compatibility, the compiler recognizes _set_background_color_ as equivalent to _setBackgroundColor. All Version 6 code that uses the old naming convention in CALL SEND or CALL NOTIFY method invocations will still function with no modification.
Although it is possible for you to name a new method using a leading underscore, you should use caution when doing so. Your method names may conflict with future releases of SAS/AF software if SAS Institute adds new methods to the parent classes.
Specifying Parameter Types and Storage Types |
When you define a method parameter, you must specify its data type. Optionally, you can also specify its storage type: input, output, or update. The storage type determines how methods can modify each other's parameters:
input | The values of the caller's parameters are copied into the corresponding parameters in the called method. When the called method's ENDMETHOD statement is executed, any updated values are not copied out to the caller's parameters. |
output | The values of the caller's parameters are not copied into the corresponding parameters in the called method. When the called method's ENDMETHOD statement is executed, any updated values are copied out to the caller's parameters. |
update | The values of the caller's parameters are copied into the corresponding parameters in the called method. When the called method's ENDMETHOD statement is executed, any updated values are copied out to the caller's parameters. |
You use the colon (:) delimiter to specify both the storage type and the data type for each method parameter:
variables<:storage>:type |
import sashelp.fsp.collection.class; class TypeStore; m1: method n:num a b:update:char return=num /(scl = 'work.a.uType.scl'); m2: method n:output:num c:i:char /(scl = 'work.b.uType.scl'); m3: method s:i:Collection /(scl = 'work.c.uType.scl'); m4: method l:o:list /(scl = 'work.d.uType.scl'); endclass;The parameter storage type and data type for each method are as follows:
Method | Parameter | Data Type | Storage |
---|---|---|---|
m1 | n | numeric | update |
a | character | update | |
b | character | update | |
m2 | n | numeric | output |
c | character | input | |
m3 | s | Collection class | input |
m4 | l | list | output |
Note: If you specify the storage type for a parameter in the CLASS block,
then you must also specify the storage type in the USECLASS block.
Passing Objects as Arguments for Methods |
Object types are treated internally as numeric values. This can affect how you overload methods. See Overloading and List, Object, and Numeric Types for more information.
Returning Values From Methods |
When you declare or implement a method, you can specify the data type of the return value with the RETURN option. If the method has a RETURN option, then the method implementation must contain a RETURN statement. The method's RETURN statement must specify a variable, expression, or value of the same type. In the following example, method m1 returns a numeric value:
class mylib.mycat.myclass.class; /* method declaration */ m1: method n:num c:char return=num; /* method implementation */ return(n+length(c)); endmethod; endclass;
Method Signatures |
A method's signature is a set of parameters that uniquely identifies the method to the SCL compiler. Method signatures enable the compiler to check method parameters at compile time and can enable your program to run more efficiently. All references to a method must conform to its signature definition. Overloaded methods must have signatures. (See Overloading Methods.)
A signature is automatically generated
for each Version
8 method unless you specify
Signature='N'
in the method's option list. By default,
Signature='Y'
for all Version 8 methods. When you edit a class in the
Build window, a signature is generated for each method that is declared in
that class when you issue the SAVECLASS command or select
File | Save as class... |
For all Version 6 methods, the default is
Signature='N'
. See Converting Version 6 Non-Visual Classes to Version 8 Classes
for information about adding signatures to Version 6 methods.
For example, the following method declarations show methods that have different signatures:
Method1: method name:char number:num; Method2: method number:num name:char; Method3: method name:char; Method4: method return=num;Each method signature is a unique combination, varying by argument number and type:
Method1 sigstring: (CN)V Method2 sigstring: (NC)V Method3 sigstring: (C)V Method4 sigstring: ()N
The order of arguments also determines the method signature. For example, the getColNum methods below have different signatures -- (CN)V and (NC)V -- because the arguments are reversed. As a result, they are invoked differently, but they return the same result.
/* method1 */ getColNum: method colname:char number:update:num; number = getnitemn(listid, colname, 1, 1, 0); endmethod; /* method2 */ getColNum: method number:update:num colname:char; number = getnitemn(listid, colname, 1, 1, 0); endmethod;
You can also use the Class Editor to define method signatures. See
SAS Guide to Applications Development
for more information.
A sigstring has the following compressed form:
(<argument-type-1 argument-type-2...argument-type-n>)return-type |
N | Numeric |
C | Character string |
L | SCL list |
O | Generic object |
O:<class-name>; | Specific class. The class name should
be preceded by
O: and followed
by a semi-colon. |
Return-type can be any
of the above types, or
V
for void, which specifies that the method does not return a value. The return
type cannot be an array.
Arrays are shown by preceding any of the above types
with a bracket ( [ ). For example, a method that receives a numeric value
and an array of characters and returns a numeric value would have the signature
(N[C)N
.
Here are some examples of method signatures:
()V
. This sigstring is the default signature.
(NCL)N
.
(O:sashelp.classes.programHalt.class;N)V
(OC)C
.
Note: Although the return type is listed as part of
the sigstring, it is not used by SCL to identify the method.
Therefore, it is recommended that you do not define methods that differ only
in return type. See Overloading Methods
for more information.
For example, suppose your program contains the following class:
class Sig; /* Signature is (N)C */ M1: method n:num return=char /(scl='work.a.uSig.scl'); /* Signature is ([C)V */ M1: private method n(*):char /(scl='work.a.uSig.scl'); /* Signature is ()V */ M1: protected method /(scl='work.a.uSig.scl'); /* Signature is (NC)V M1: method n:num c:char /(scl='work.a.uSig.scl'); endclass;Suppose also that your program calls M1 as follows:
dcl char ch; ch = M1(3);SCL will call the method with the signature (N)C. If your program calls M1 like this:
M1();SCL will call the method with the signature ()V.
Forward-Referencing Methods |
Within
a CLASS block, if a method invokes a another method within that same class,
then either the second method must be implemented before the first, or the
second method must be declared with the Forward='Y'
option.
Note: Any methods that are forward-referenced must be implemented
in the class in which they are declared.
In the following example, m1 calls m2, so the compiler needs to know the existence of m2 before it can compile m1.
class mylib.mycat.forward.class; m2: method n:num c:char return=num / (forward='y'); m1: method n1 n2:num mylist:list return=num; dcl num listLen = listlen(mylist); dcl num retVal; if (listLen = 1) then retVal=m2(n1,'abc'); else if (listLen = 2) then retVal=m2(n2,'abc'); endmethod; m2:method n:num c:char return=num; return(n+length(c)); endmethod; endclass;
Overloading Methods |
All overloaded methods must have method signatures because SCL uses the signatures to differentiate between overloaded methods. If you call an overloaded method, SCL checks the method arguments, scans the signatures for a match, and executes the appropriate code. A method that has no signature cannot be overloaded.
If you overload a method, and the signatures differ only in the return type, the results are unpredictable. The compiler will use the first version of the method that it finds to validate the method. If the compiler finds the incorrect version, it generates an error. If your program compiles without errors, then when you run the program, SCL will execute the first version of the method that it finds. If it finds the incorrect version, SCL generates an error. If it finds the correct version, your program might run normally.
Each method in a set of overloaded methods can have
a different scope, as well. However, the scope is not considered part of the
signature, so you may not define two methods that differ only by scope. (See Defining Method Scope.)
Suppose you have the following two methods, where each method performs a different operation on its arguments:
CombineNumerics: public method a :num b :num return=num; endmethod; CombineStrings: public method c :char d :char return=char; endmethod;Assume that CombineNumerics adds the values of A and B, whereas CombineStrings concatenates the values of C and D. In general terms, these two methods combine two pieces of data in different ways based on their data types.
Using method overloading, these methods could become
Combine: public method a :num b :num return=num; endmethod; Combine: public method c :char d :char return=char; endmethod;
In this case, the Combine method is overloaded with two different parameter lists: one that takes two numeric values and returns a numeric value, and another that takes two character parameters and returns a character value.
As a result, you have defined two methods that have the same name but different parameter types. With this simple change, you do not have to worry about which method to call. The Combine method can be called with either set of arguments, and SCL will determine which method is the correct one to use, based on the arguments that are supplied in the method call. If the arguments are numeric, SCL calls the first version shown above. If the arguments are character, SCL calls the second version. The caller can essentially view the two separate methods as one method that can operate on different types of data.
Here is a more complete example that shows how method overloading fits in with the class syntax. Suppose you create X.SCL and issue the SAVECLASS command, which generates the X class. (Although it is true here, it is not necessary that the class name match the entry name.)
class X; Combine: public method a:num b:num return=num; dcl num value; value = a + b; return value; endmethod; Combine: public method a:char b:char return=char; dcl char value; value = a || b; return value; endmethod; endclass;
You can then create another entry, Y.SCL. When you compile and execute Y.SCL, it instantiates the X class and calls each of the Combine methods.
import X.class; init: dcl num n; dcl char c; dcl X xobject = _new_ X(); n = xobject.Combine(1,2); c = xobject.Combine("abc","def"); put n= c=;
The PUT statement produces
n=3 c=abcdef
Another typical use of method overloading is to create methods that have optional parameters.
Note: This example shows two
implementations of an overloaded method that each accept different numbers
of parameters. Defining One Implementation That Accepts Optional Parameters
describes how to use the OPTIONAL option to create a method with one implementation
that accepts different numbers of parameters.
For example, suppose
we have a method that takes a character string and a numeric value, where
the numeric value is used as a flag to indicate a particular action. The method
signature would be (CN)V.
M: public method c :char f :num; if (f = 1) then /* something */ else if (f = 2) /* something else */ else /* another thing */ endmethod;
If method M is usually called with the flag equal to one, you can overload M as (C)V, where that method would simply include a call to the original M. The flag becomes an optional parameter.
M: public method c: char; M(c, 1); endmethod;
When you want the flag to be equal to one, call M with
only a character string parameter. Notice that this is not an error. Method
M can be called with either a single character string, or with a character
string and a numeric -- this is the essence of method overloading. Also,
the call
M(c,1);
is not a recursive call with an incorrect parameter list. It is a
call to the original method M.
This example can also be turned around for cases with existing code. Assume that we originally had the method M with signature (C)V and that it did all the work.
M: public method c: char; /* A lot of code for processing C. */ endmethod;
Suppose you wanted to add an optional flag parameter, but did not want to change the (possibly many) existing calls to M. All you need to do is overload M with (CN)V and write the methods as follows:
M: public method c: char f: num; Common(c, f); endmethod; M: public method c: char; Common(c, 0); endmethod; Common: public method c: char f: num; if (f) then /* Do something extra. */ /* Fall through to same old code for */ /* processing S. */ endmethod;
Notice that when you call M with a single character
string, you get the old behavior. When you call M with a string and a (non-zero)
flag parameter, you get the optional behavior.
You can use the OPTIONAL option to create an overloaded method with only one implementation that will accept different numbers of parameters, depending on which arguments are passed to it.
In the following example, the method M1 will accept from two to four parameters:
class a; M1: public method p1:input:num p2:output:char optional=p3:num p4:char / (scl='mylib.classes.old.scl'); endclass;SCL will generate three signatures for this method:
(NC)V | |
(NCN)V | |
(NCNC)V |
Lists and objects (variables declared with either the OBJECT keyword or a specific class name) are treated internally as Numeric values. As a result, in certain situations, variables of type List, Numeric, generic Object, and specific class names are interchangeable. For example, you can assign a generic Object or List to a variable that has been declared as Numeric, or you can assign a generic Object to a List. This flexibility enables Version 6 programs in which list identifiers are stored as Numeric variables to remain compatible with Version 8.
The equivalence between objects, lists, and numeric variables requires that you exercise caution when overloading methods with these types of parameters. When attempting to match a method signature, the compiler first attempts to find the best possible match by matching the most parameter types exactly. If no exact match can be found, the compiler resorts to using the equivalence between List, generic Object, and Numeric types.
For example, suppose you have a method M with a single signature (L)V. If you pass a numeric value, a list, or an object, it will be matched, and method M will be called. If you overload M with signature (N)V, then Numeric values will match the signature (N)V, and List values will match the signature (L)V. However, List values that are undeclared or declared as Numeric will now match the wrong method. Therefore, you must explicitly declare them with the LIST keyword to make this example work correctly. Also, if you pass an object, it will match both (L)V and (N)V, so the compiler cannot determine which method to call and will generate an error message.
Overriding Existing Methods |
When
you instantiate a class, the new class (or subclass) inherits the methods
of the parent class. If you want to use the signature of one of the parent's
methods, but you want to replace the implementation with your own implementation,
you can override the parent's method. To override the
implementation of a method, specify State='O'
in the method declaration and in the method implementation. Here is an example
for a class named State:
class State; _init: method / (state='o'); _super(); endmethod; endclass;
Defining Constructors |
Constructors are methods that are used to initialize an instance of a class. The Object class provides a default constructor that is inherited for all classes. Unless your class requires special initialization, you do not need to create a constructor.
Each constructor has the following characteristics:
Note: Using the _NEW_ operator to instantiate a class is the only way
to run constructors. Unlike other user-defined methods, you cannot execute
constructors using dot notation. If you instantiate a class in any way other
than by using the _NEW_ operator (for example, with the _NEO_ operator), constructors
are not executed.
For example, you could define a constructor X for class X as follows:
class X; X: method n: num; put 'In constructor, n='; endmethod; endclass;You can instantiate the class as follows:
init: dcl X x = _new_ X(99); return;The constructor is run automatically when the class is instantiated. The argument to _NEW_, 99, is passed to the constructor. The output is
In constructor, n=99
Like other methods, constructors can be overloaded. Any void method that has the same name as the class is treated as a constructor. The _NEW_ operator determines which constructor to call based on the arguments that are passed to it. For example, the Complex class defines two constructors. The first constructor initializes a complex number with an ordered pair of real numbers. The second constructor initializes a complex number with another complex number.
class Complex; private num a b; Complex: method r1: num r2: num; a = r1; b = r2; endmethod; Complex: method c: complex; a = c.a; b = c.b; endmethod; endclass;This class can be instantiated with either of the following statements:
dcl Complex c = _new_(1,2); dcl Complex c2 = _new_(c);These statements both create complex numbers. Both numbers are equal to
1 + 2i
.
The
default constructor does not take any arguments. If you want to create your
own constructor that does not take any arguments, you must explicitly override
the default constructor. To override the default constructor, specify State='o'
in the method options list.
class X; X: method /(state='o'); ...SCL statements to initialize class X... endmethod; endclass;
Constructors can be called explicitly only from other constructors. The _NEW_ operator calls the first constructor. The first constructor can call the second constructor, and so on.
When a constructor calls another constructor within the same class, it must use the _SELF_ system variable. For example, you could overload X as follows:
class X; private num m; X: method n: num; _self_(n, 1); endmethod; X: method n1: num n2: num; m = n1 + n2; endmethod; endclass;The first constructor, which takes one argument, calls the second constructor, which takes two arguments, and passes in the constant
1
for the second argument.
The following labeled section creates two instances of
X. In the first
instance, the m
attribute is set to 3
. In the second instance, the m
attribute is set to 100
.
init: dcl X x = _new_ X(1,2); dcl X x2 = _new_ X(99); return;
Constructors can call parent constructors by using the _SUPER operator. For example, suppose you define class X as follows:
class X; protected num m; X: method n: num; m = n * 2; endmethod; endclass;Then, you create a subclass Y whose parent class is X. The constructor for Y overrides the default constructor for Y and calls the constructor for its parent class, X.
class Y extends X; public num p; Y: method n: num /(state='o'); _super(n); p = m - 1; endmethod; endclass;You can instantiate Y as shown in the following labeled section. In this example, the constructor in Y is called with argument
10
. This value is passed to the constructor in X, which uses
it to initialize the m
attribute to 20
. Y then initializes the p
attribute to 19
.
init: dcl Y y = _new_ Y(10); put y.p=; return;The output would be:
y.p=19
Note: As with other overridden methods that have identical signatures,
you must explicitly override the constructor in Y because there is a constructor
in X that has the same signature.
The compiler
automatically treats as a constructor any void method
that has the same name as the class. If you do not want such a method to
be treated as a constructor, you can specify constructor='n'
in the method declaration.
class X; X: method /(constructor='n'); put 'Is not constructor'; endmethod; endclass; init: dcl X x = _new_ X(); put 'After constructor'; x.x(); return;This will result in the following output:
After constructor Is not constructor
Implementing Methods Outside of Classes |
To define class methods in a different SCL entry, use the USECLASS statement block. The USECLASS block binds methods that it contains to the class that is specified in the USECLASS statement. The USECLASS statement also enables you to define implementations for overloading methods. (See Overloading Methods. )
Method implementations inside a USECLASS block can include any SCL functions and routines. However, the only SCL statements that are allowed in USECLASS blocks are METHOD statements.
The USECLASS block binds the methods that it contains to a class that is defined in a CLASS statement block or in the Class Editor. Therefore, all references to the methods and the attributes of the class can bypass references to the _SELF_ variable completely as long as no ambiguity problem is created. Because the binding occurs at compile time, the SCL compiler can detect whether an undefined variable is a local variable or a class attribute. See also Referencing Class Methods or Attributes.
Method Metadata |
init: DCL num rc metadata; DCL object obj; obj=loadclass('class-name'); /* metadata is a numeric list identifier */ rc=obj._getMethod('getMaxNum',metadata); call putlist(metadata,'',2); return;
Chapter Contents |
Previous |
Next |
Top of Page |
Copyright 1999 by SAS Institute Inc., Cary, NC, USA. All rights reserved.