Chapter 8

Initializing an Object

Whenever a Telescript engine creates an object, it initializes the object--it sets the values of the object's properties to give it a start in the world. You, as the programmer who asked for the birth of the object, may be required to provide some of those values.

This chapter introduces you to the basics of initialization so you can understand what values you'll need for initialization and how those values are used. You'll learn about initialization arguments and escalation in initialization. To help you instantiate predefined Telescript classes, you'll see how a class description lists necessary arguments. And--because some of the objects you create may be the products of class families--you'll learn how to derive a class from a class family and then instantiate that class.

An Overview of Initialization

Let's start with a flashback to Chapter 2, where you learned that an object is an instance of a class. As you'll recall, the class definition contains the object's methods, operations, and attributes. The object itself is simply a collection of properties--each with its own value. The values of an object's properties determine the object's state.

When the Telescript engine creates a new object by instantiating a class, it must provide a value for each of the new object's properties. Left to its own devices, the engine simply provides a nil for each property. A set of nil properties may be okay for some newborn objects, but objects of many classes must start their lives with properties set to non-nil values. The Telescript mechanism that provides for a set of non-nil values on an object's birth is the initialization operation.

The Initialization Operation

Every flavor class has an initialization operation whose identifier is initialize. The class may define the operation itself, or it may inherit the initialization method from its immediate superclass. The initialization operation may or may not require arguments. Its job is--at its discretion--to set the value of any of the new object's properties. The operation may set a property to a default value, an argument value, or a calculated value. If the initialization operation doesn't set the value of a property, the property has a value of nil. Abstract classes, because they are never instantiated, don't need to have an initialization operation. They may have one, however, to set properties when initialization is escalated as described later in this chapter.

You'll learn the intricacies of defining an initialization operation when you learn to define your own classes in the next section of this book. For now, it's enough to know in general what an initialization operation does.

Constructor Expressions

In Chapter 3 you learned about constructor expressions and literal expressions. Both, when executed, create instances of objects. A literal expression, as you may recall, is a standalone value that is turned into an instance of the appropriate primitive (or other simple class such as String or BitString). You can only instantiate primitive classes using a literal expression; the compiler will balk if you try to create a primitive using a constructor expression. When the engine executes a literal expression, it initializes a corresponding object with the value contained in the expression.

All non-primitive non-abstract classes can be instantiated using a constructor expression. A constructor expression accepts zero or more arguments and, when executed, starts the process of initialization. Its syntax is:

<class name>(<optional arguments>)
The class name specifies the class you want to instantiate. The parentheses enclose initialization arguments which, as operation arguments, must be separated by commas if there are more than one. It's possible to have no initialization arguments, in which case the parentheses are empty.

When a constructor expression is executed, the engine instantiates the specified class, setting all properties to nil. It then calls initialize on the newly created object using the arguments you supply in the constructor expression. You must provide the proper arguments for initialization just as you must supply the proper arguments for any other kind of operation. The following example shows a constructor expression that constructs an instance of the custom class TestObject using the initialization arguments 75 and true:

TestObject(75, true)

Escalation During Initialization

Once the engine calls initialize on an object, the initialize method accepts any arguments provided and then executes. The initialization method for each class, no matter how it's written, must at one point escalate the initialization operation--that is, the method calls the initialize method of the object's immediate superclass. When the escalated operation finishes, execution drops back to the method of the object's own class. (Chapter 4 discusses escalation in more detail.)

Because the initialization method of each class must escalate intialization, initialization chains up from subclass to superclass to superclass to superclass, then back down again as each initialization method finishes execution. Initialization occurs at each level of the chain.

As an example, consider the class tree in Figure 8-1. If you call initialize on an instance of the class at the bottom (StocksRecords), the initialization operation at some point escalates to the superclass FinancialRecords. As the FinancialRecords initialization executes, it escalates at some point to the mix-in superclass Currency, which escalates to the superclass DataRecorder, which in turn escalates to the root class Object. When the initialization method of Object finishes, execution returns to the lower classes in the inverse order of escalation: first to DataRecorder, then to Currency, then to FinancialRecords, and finally back to StocksRecords. When StocksRecords' initialization method finishes execution, initialization is completed.

Figure 8-1 : Initialization escalates from subclasses up to superclasses as shown by the numbers here.

Why must initialization always escalate through an object's superclasses before an object is initialized? Because, as you may remember, a class's methods can only set the properties of the class itself. They can't set the properties of any of the class's superclasses or subclasses. This means that when you instantiate a class, the class's initialize operation can only set the class's own properites; it can't set any of the properties it inherits from its superclasses. By escalating initialize to the method of each of its superclasses, the escalated initialize can set properties for each superclass. The newly-instantiated object will have all of its properties--native and inherited--set appropriately.

In the previous escalation example, when you instantiate StocksRecords, its initialize operation sets StocksRecords properties. When initialize escalates to FinancialRecords, it sets FinancialRecords properties. Each level of initialization sets properties of the current class until the Object properties are finally set and escalation returns back down through the inheritance hierarchy.

Escalation is a topic of exquisite intricacy that involves issues such as canonic order (the order of escalation through superclasses). We won't get into the details in this chapter; you can read about them in Chapter <<<yet to be written>>> where you learn to create your own methods. For now, it's enough to know that whenever you create an object, its initialization works at the level of each of its superclasses. Which leads to some interesting questions about the arguments you provide for initialization...

Arguments and Escalation

The initialize operation of each class has its own required arguments, which may range in number from many to none. When you instantiate an object, you supply one set of arguments. How are these arguments passed on to each level of escalation?

The Telescript engine maintains a LIFO stack of arguments for initialize. It reads the provided arguments from right to left and pushes them onto the stack (which means that the first argument is at the top of the stack). When initialize runs for the instantiated class, the initialize method pops off the arguments it requires and leaves the other arguments on the stack. When initialize escalates to the method of the immediate superclass, that method pops off the arguments it requires. Each level of escalation pops off the appropriate number of arguments from the top of the stack until escalation is finished and all the arguments are used.

Not all escalations are as simple as the account above--you'll find some interesting wrinkles in different classes. Many superclasses require no arguments, so they take no arguments off the stack before escalation. This simplifies the number of arguments required for construction. Some superclasses have no initialize operation at all. In this case, the engine simply escalates initialize to the next superclass, passing the untouched stack with it. A few classes use their initialize method to calculate one or more values for escalation, then push the values on the stack for escalation. In such a case, you won't supply those initialization values in the constructor expression.

Providing Initialization Arguments

Now that you know how initialization escalates and how initialization arguments are necessary for each level of escalation, let's look at the practical side of initialization: How do you know what arguments to supply for initialization?

Constructor Information for Predefined Classes

For Telescript predefined classes, you'll find constructor information for each class in Part Four of the Telescript Language Reference. Each class description includes an operation signature for initialize under the heading "Constructor." This signature, like any other operation signature, shows the argument identifiers and constraints necessary for initialization. (These arguments include those necessary for escalation.) It may also show exceptions that can be thrown on initialization.

The Constructor section of a class description explains the meaning of the initialization arguments required for that class. You may also see a table of the class's attributes, each associated with a default value and an argument that sets a new value. If you don't set these attributes (in other words, you supply a nil for their arguments or the attributes are not allowed as arguments), they are set to their default values.

Arguments that are provided for escalation to superclasses aren't always fully explained in the class description; you'll find explanations for these arguments in the Constructor section of the appropriate superclasses.

You may occasionally come across a class description with no Constructor section. In this case, the class's initialization operation is the same as that of its immediate superclass--so you can find constructor information by looking up the superclass. Or the class may be an abstract class, in which case you can't instantiate the class anyway.

Constructor Information for Custom Classes

If you're working with custom classes that you've defined yourself or have received from other sources, you can look in header files for class definitions. The class definition for each flavor class (and some mix-in classes) often has a signature for the initialize operation. (If it doesn't, it inherits its initialize operation from its immediate superclass.) The definition shows the arguments necessary for a constructor expression along with the constraint for each argument. You may have to peek through superclass definitions to figure out what each argument does unless you're lucky enough to have some documentation that discusses initialize arguments.

You can find full information about class definitions and operation signatures in the next section of this book <<<yet to be written>>> where you learn to create your own custom classes.

Initializing an Object From a Class Family

As you'll recall from Chapter 2, a class family is not a kind of class--it's a class-producing function. Many commonly used Telescript classes are derived from class families. For example, consider the class family Collection, which you'll learn about in detail in the next chapter. It's used to derive a variety of parallel classes, each of which can hold objects of a different type and compare those objects in a specified way.

When you create an instance of a derived class, you must first derive the class from the class family. You can then provide initialization arguments for the object. To derive a class from a class family, you provide an actual parameter for each of the formal parameters defined by the class family.

Understanding Formal and Actual Parameters

To derive a class, you should have a general understanding of the way a class family uses formal and actual parameters. What follows is a brief overview of formal and actual parameters. (If you want complete details, you'll find them in Chapter <<<yet to be written>>> where you learn to create your own class families.)

A class family definition starts with a list of formal parameters that it uses throughout the rest of the definition. Each formal parameter is a unique identifier that can be used to specify a class almost anywhere in the class family definition. You can think of the formal parameter as a placeholder--a class variable, if you will. The classes within the class family definition that are specified with formal parameters aren't determined until a class is derived from a class family.

When you derive a class from a class family, you supply one actual parameter for each formal parameter of the class family. Each actual parameter is a class, which you specify with a class name. When the engine derives a class, it creates the class definition by copying the class family's definition and replacing any formal parameter with the class supplied by the corresponding actual parameter. The resulting derived class has no formal parameters; the classes are all specifically defined using the actual parameters.

Actual Parameter Syntax

You supply actual parameters for a class family in a constructor expression. The expression instantiates a class derived from the class family; it uses this syntax:

<class family name>[<one or more actuals>](<optional arguments>)
The class family name is followed by a pair of square brackets ([]) that contain actuals. You may have one or more actuals--it depends on the definition of the class family. You must have one actual parameter for each formal parameter of the class family. The actuals are listed in the same order in which their corresponding formals are defined; the actuals are separated by commas.

A pair of parentheses follows the square brackets. These enclose any arguments necessary for initializing the derived class, and may be empty if there are no arguments.

As an example, consider the Telescript class family Collection. It has two formal parameters: Item and Match. The formal parameter Item is the class of the objects that may be contained by a Collection object. The parameter Match stands for the mix-in class that determines how objects in the collection are compared. If you derive a class from Collection, you might supply the actual parameters Number and Equal as shown in the following constructor:

Collection[Number, Equal](36, 100.5, 5, 27)
This expression uses the actuals Number and Equal to derive a class from Collection. The derived class is referred to as Collection[Number, Equal]. The class Number (the first actual parameter) satisfies the formal parameter Item, so the class is defined to contain a collection of numbers (objects that are members of the class Number). The class Equal (the second actual parameter) satisfies the formal parameter Match, so the class is defined to compare its collection objects in the way defined by the Equal mix-in (as opposed to the way defined by the Same mix-in, one of the other classes you can use for the second parameter).

The expression ends with a set of four numbers in parentheses. These numbers are the arguments required for initializing a Collection-derived class. They become the objects contained by the instance of Collection[Number, Equal] created by this expression.

Class Family Signatures

When you look at a class family description in the class reference documentation, you'll find the class signature of the class family under the heading "Class." This signature shows the class family's formal parameters; they're enclosed in square brackets ([]). Take a look at the Telescript class family Collection as an example. The first part of its class signature is

Collection: interface[Item: Class; Match: Class<:Compared]
The declaration of the formal parameters within square brackets are separated from each other by semicolons (;). Each declaration starts with an identifier followed by a colon and the key word Class, which is the beginning of a constraint that specifies what classes can satisfy the formal parameter. In this example, there are two formal parameters: Item and Match. Item is followed by the constraint Class, Match is followed by the constraint Class<:Compared.

If a formal parameter has a constraint that has no class listed--just the keyword Class followed imediately by a semicolon, then the formal parameter can be satisfied with any class--flavor or mix-in. The formal parameter Item in the previous example is a case in point. It ends in Class;, so it can be satisfied with an actual parameter that is any class.

If a formal parameter has a constraint that lists a class following the keyword Class and the symbol <:, then the actual parameter is constrained: it must be the class itself or one of its subclasses. The formal parameter Match in the previous example uses this convention. Because it ends in <:Compared, the actual parameter is constrained to a class that is either Compared or a subclass of Compared. This means that the formal can only be satisfied by Compared or either of its two subclasses, Equal or Same.

Note that a class family signature can list two or more formal parameters with a single constraint, just as an operation signature can list two or more arguments with a single constraint. Look at the first part of the class signature for the class Dictionary:

Dictionary: interface[Key, Value: Class; Match: Class<:Compared]
The two formals Key and Value both share the constraint Class.

Class Family Initialization Arguments

The arguments required to initialize a derived class may vary in constraint depending on the actual parameters used to derive the class. For example, if you derive a Collection class to be Collection[Number, Equal], the initialization arguments for the class must be members of the class Number because the derived class is defined to handle Number objects. If you derive the Collection class Collection[Character, Equal], the initialization arguments must be members of the class Character, not class Number.

If you look at the initialize operation signature in a class family description, you may sometimes find that a constraint for an argument is defined using a formal parameter. When you derive a class, that constraint takes the class of whatever actual parameter you supplied for the formal parameter. When you supply initialization arguments, you must make sure they match the actual constraints of the derived class.

As an example, consider the initialize operation signature for the class family Collection:

initialize: op (items: Item ...);
The argument items is constrained by the formal parameter Item, which was presented in the class signature you looked at earlier. If you derive the class Collection[Number, Equal] from the Collection class family, items' constraint is set to Number, so the variable arguments you supply for items must all be members of the class Number.

Default Derived Classes

Class families have default actual parameters; if you use a class family name without specifying actual parameters, you implicitly derive a class using the default parameters.

Custom Class Families

For custom class families, the default actuals are always the class Object no matter how the formal parameters are constrained. For example, a custom class family with this at the beginning of its signature

Census: interface[Unit: Class; Population: Class<:Number]
has two formals: Unit, which is constrained to any class (flavor or mix-in) and Population, which is constrained to Number or either of its two subclasses, Real or Integer. If you instantiate the family without providing actuals (that is, you leave out the square brackets and anything they might enclose), then you realize a class using the family's default actuals: both are Object. For example, this constructor expression

Census(67, report)
derives the class Census[Object, Object] initialized with the values 67 and report--even though the formal Population is constrained to be a number, not an object.

Built-in Class Families

Built-in Telescript class families follow their constraints when the engine determines default actual parameters. Any constraint without a specified class (Class alone, in other words) has a default of Object. Any constraint with a specified class (Class<:Compared, for example) has a default that is the specified class (Compared for the last example).

As a real example, consider the first part of the Collection class signature once again:

Collection: interface[Item: Class; Match: Class<:Compared]
The default actual parameter for Item is class Object; the default actual parameter for Match is the class Compared. If you use Collection in a constructor without specifying actuals as in this example

Collection('A', 5, 2.3)
you derive the class Collection[Object, Compared] and pass three objects as initialization arguments.

* * *

Now that you've learned initialization techniques and had a closer look at the characteristics of class families, you can learn more about some of the most commonly used class families in Telescript: the class family Collection and its subclasses.

8 - Initializing an Object
An Overview of Initialization
The Initialization Operation
Constructor Expressions
Escalation During Initialization
Arguments and Escalation
Providing Initialization Arguments
Constructor Information for Predefined Classes
Constructor Information for Custom Classes
Initializing an Object From a Class Family
Understanding Formal and Actual Parameters
Actual Parameter Syntax
Class Family Signatures
Class Family Initialization Arguments
Default Derived Classes
Custom Class Families
Built-in Class Families

TS Ref - 26 JUN 1996

Generated with Harlequin WebMaker