Methods and Constructors
Instructions in NetRexx are grouped into methods, which are named routines that always belong to (are part of) a class.
Methods are invoked by being referenced in a term, which may be part of an expression or be a clause in its own right (a method call instruction). In either case, the syntax used for a method invocation is:
symbol([expression[,expression]...])
The symbol, which must be non-numeric, is called the name of the method. It is important to note that the name of the method must be followed immediately by the '(', with no blank in between, or the construct will not be recognized as a method call (a blank operator would be assumed at that point instead).
The expressions (separated by commas) between the parentheses are called the arguments to the method. Each argument expression may include further method calls.
The argument expressions are evaluated in turn from left to right and the resulting values are then passed to the method (the procedure for locating the method is described below). The method then executes some algorithm (usually dependent on any arguments passed, though arguments are not mandatory) and will eventually return a value. This value is then included in the original expression just as though the entire method reference had been replaced by the name of a variable whose value is that returned data.
For example, the substr method is provided for strings of type Rexx and could be used as:
c='abcdefghijk'
a=c.substr(3,7)
/* would set A to "cdefghi" */
Here, the value of the variable c is a string (of type Rexx). The substr (substring) method of the Rexx class is then invoked, with arguments 3 and 7, on the value referred to by c. That is, the the properties available to (the context of) the substr method are the properties constructed from the literal string 'abcdefghijk'. The method returns the substring of the value, starting at the third character and of length seven characters.
A method may have a variable number of arguments: only those required need be specified. For example, 'ABCDEF'.substr(4) would return the string 'DEF', as the substr method will assume that the remainder of the string is to be returned if no length is provided.
Method invocations that take no arguments may omit the (empty) parentheses in circumstances where this would not be ambiguous. See the section on Terms for details.
Implementation minimum: At least 10 argument expressions should be allowed in a method call.
Method call instructions
When a clause in a method consists of just a term, and the final part of the term is a method invocation, the clause is a method call instruction:
symbol([expression[,expression]...]);
The method is being called as a subroutine of the current method, and any returned value is discarded. In this case (and in this case only), the method invoked need not return a value (that is, the return instruction that ends it need not specify an expression).[1]
A method call instruction that is the first instruction in a constructor (see below) can only invoke the special constructors this and super.
Method resolution (search order)
Method resolution in NetRexx proceeds as follows:
- If the method invocation is the first part (stub) of a term, then:
- The current class is searched for the method (see below for details of searching).
- If not found in the current class, then the superclasses of the current class are searched, starting with the class that the current class extends.
- If still not found, then the classes listed in the uses phrase of the class instruction are searched for the method, which in this case must be a static method. Each class from the list is searched for the method, and then its superclasses are searched upwards from the class; this process is repeated for each of the classes, in the order specified in the list.
- If still not found, the method invocation must be a constructor (see below) and so the method name, which may be qualified by a package name, should match the name of a primitive type or a known class (type). The specified class is then searched for a constructor that matches the method invocation.
- If the method invocation is not the first part of the term, then the evaluation of the parts of the term to the left of the method invocation will have resulted in a value (or just a type), which will have a known type (the continuation type). Then:
- The class that defines the continuation type is searched for the method (see below for details of searching).
- If not found in that class, then the superclasses of that class are searched, starting with the class that that class extends.
If the search did not find a method, an error is reported.
If the search did find a method, that is the method which is invoked, except in one case: - If the evaluation so far has resulted in a value (an object), then that value may have a type which is a subclass of the continuation type. If, within that subclass, there is a method that exactly overrides the method that was found in the search, then the method in the subclass is invoked.
This case occurs when an object is earlier assigned to a variable of a type which is a superclass of the type of the object. This type simplification hides the real type of the object from the language processor, though it can be determined when the program is executed.
Searching for a method in a class proceeds as follows:
- Candidate methods in the class are selected. To be a candidate method:
- the method must have the same name as the method invocation (independent of the case of the letters of the name)
- the method must have the same number of arguments as the method invocation (or more arguments, provided that the remainder are shown as optional in the method definition)
- it must be possible to assign the result of each argument expression to the type of the corresponding argument in the method definition (if strict type checking is in effect, the types must match exactly).
- If there are no candidate methods then the search is complete; the method was not found.
- If there is just one candidate method, that method is used; the search is complete.
- If there is more than one candidate method, the sum of the costs of the conversions from the type of each argument expression to the type of the corresponding argument defined for the method is computed for each candidate method.
- The costs of those candidates (if any) whose names match the method invocation exactly, including in case, are compared; if one has a lower cost than all others, that method is used and the search is complete.
- The costs of all the candidates are compared; if one has a lower cost than all others, that method is used and the search is complete.
- If there remain two or more candidates with the same minimum cost, the method invocation is ambiguous, and an error is reported.
Note: When a method is found in a class, superclasses of that class are not searched for methods, even though a lower-cost method may exist in a superclass.
Method overriding
A method is said to exactly override a method in another class if
- the method in the other class has the same name as the current method
- the method in the other class is not private
- the other class is a superclass of the current class, or is a class that the current class implements (or is a superclass of one of those classes).
- the number and type of the arguments of the method in the other class exactly match the number and type of the arguments of the current method (where subsets are also checked, if either method has optional arguments).
For example, the Rexx class includes a substr method, which takes from one to three strings of type Rexx. In the class:
class mystring extends Rexx
method substr(n=Rexx, length=Rexx)
return this.reverse.substr(n, length)
method substr(n=int, length=int)
return this.reverse.substr(Rexx n, Rexx length)
the first method exactly overrides the substr method in the Rexx class, but the second does not, because the types of the arguments do not match.
A method that exactly overrides a method is assumed to be an extension of the overridden method, to be used in the same way. For such a method, the following rules apply:
- It must return a value of the same type as the overridden method (or none, if the overridden method returns none).
- It must be at least as visible as the overridden routine. For example, if the overridden routine is public then it must also be public.
- If the overridden method is static then it must also be static.
- If the overridden method is not static then it must not be static.
- If the underlying implementation checks exceptions, only those checked exceptions that are signalled by the overridden method may be left uncaught in the current method.
Constructor methods
As described above, methods are usually invoked in the context of an existing value or type. A special kind of method, called a constructor method, is used to actually create a value of a given type (an object).
Constructor methods always have the same short name as the class in which they are found, and construct and return a value of the type defined by that class (sometimes known as an instance of that class). If the class is part of a package, then the constructor call may be qualified by the package name.
Example constructors:
File('Dan.yr.Ogof')
java.io.File('Speleogroup.letter')
Rexx('some words')
netrexx.lang.Rexx(1)There will always be at least one constructor if values can be created for a class. NetRexx will add a default public constructor that takes no arguments if no constructors are provided, unless the components of the class are all static or constant, or the class is an interface class.
All constructors follow the same rules as other methods, and in addition:
- Constructor calls always include parentheses in the syntax, even if no arguments are supplied. This distinguishes them from a reference to the type of the same name.
- Constructors must call a constructor of their superclass (the class they extend) before they carry out any initialization of their own. This is so any initialization carried out by the superclass takes place, and at the appropriate moment. Only after this call is complete can they make any reference to the special words this or super.
Therefore, the first instruction in a constructor must be either a call to the superclass, using the special constructor super() (with optional arguments), or a call to to another constructor in the same class, using the special constructor this() (with optional arguments). In the latter case, eventually a constructor that explicitly calls super() will be invoked and the chain of local constructor calls ends.
As a convenience, NetRexx will add a default call to super(), with no arguments, if the first instruction in a constructor is not a call to this() or super(). - The properties of a constructed value are initialized, in the order given in the program, after the call to super() (whether implicit or explicit).
- By definition, constructors create a value (object) whose type is defined by the current class, and then return that value for use. Therefore, the returns keyword on the method instruction that introduces the constructor is optional (if given, the type specified must be that of the class). Similarly, the only possible forms of the return instruction used in a constructor are either 'return this;', which returns the value that has just been constructed, or just 'return;', in which case, the 'this' is assumed (this form will be assumed at the end of a method, as usual, if necessary).
Here is an example of a class with two constructors, showing the use of this() and super(), and taking advantage of some of the assumptions:
class MyChars extends SomeClass
properties private
/* the data 'in' the object */
value=char[]
/* construct the object from a char array */
method MyChars(array=char[])
/* initialize superclass */
super()
value=array -- save the value
/* construct the object from a String */
method MyChars(s=String)
/* convert to char[] and use the above */
this(s.toCharArray())
Objects of type MyChars could then be created thus:
myvar=MyChars("From a string")or by using an argument that has type char[].