Objects are usually models of conceptual or real-world entities; they consist of a combination of data, which models the state of the entity and operations which model the behavior of the entity. The body of a Sather class consists of a list of features which define the data and behavior of the class. A class defines a new type and may be used to create object instances of that type.[1]
[1] This is only true for reference, immutable and some kinds of external classes. Abstract a, partial and most external classes cannot have instances.
We will start by describing the data elements and then move on to the operations. In subsequent sections, we will describe the definition of object behavior in the form of routines. We will then point out that Sather provides a level of abstraction, which permits the state and behavior of the object to be treated in a uniform manner. Finally, we will describe the somewhat unusual meaning of assignment in Sather that makes this uniformity possible.
The state of a class is defined by attributes, which are have the prefix attr:
class POINT is attr x:INT; attr y:INT; end; |
The POINT class above defines an 'x' and a 'y' attribute both of which are integers. This class is useless, as it stands, since it provides no way to create instances of itself.
To make objects of the POINT class, we have to introduce a create routine
class POINT is attr x, y:INT; create(xvalue,yvalue:INT):POINT is res:POINT := new; res.x := xvalue; res.y := yvalue; return res; end; end; |
The create routine first calls the special expression 'new'. 'new' creates a new uninitialized instance of the POINT class and returns it. All the attributes in the new instance have default 'void' values. It then assigns the 'x' and 'y' attributes of this new instance to xvalue and yvalue respectively. Instances of the POINT class can then be created as shown below
p:POINT := POINT::create(3,5); |
Since creation is such a common operation, Sather provides a special shorthand for calls to the routine 'create'. The 'create' routine shown could be invoked with the # sign as shown below
point:POINT := #POINT(3,5); |
Expressions using the # sign are referred to as creation expressions, and are a convenient shorthand used for creating new objects and initializing their attributes.
When an object of the class POINT is created, the 'x' and 'y' attributes may be accessed by 'dotting' into the object.
a:POINT := #POINT(3,5); -- Create a new point #OUT + a.x ; -- Prints out the value of 'x', which is 3 a.x := 5; -- Sets the value of the 'x' attribute to 5 |
The semantics of a class is independent of the textual order of its class elements. In particular, the actual attribute layout used by a Sather implementation is invisible to a programmer.
The scope of feature names is the class body
Feature names may be either lower or upper case.
Class names must be all upper case letters (underscores and digits are permitted except as the first character).
The feature namespace is separate from the class namespace.
The scope of class names is the entire program; no two classes can have the same name (unless they have different number of parameters, which will be explained in the chapter on class parametrization).
You have to explicitly call 'new' in the create routine. The following code exhibits a common error:
class POINT is attr x,y:INT; create(xval, yval:INT):POINT is x := xval; -- Run time error! We have no object as yet! y := yval; end; end; |
Before a variable is assigned to an object, the variable has the void value. The expression 'void' may be used to determine whether a value is void or not.
The following example will print out the string "a is void!" since a POINT is a reference class and 'a' has not been created.
a:POINT; if void(a) then #OUT + "a is void!"; end; |
In the second version, the string "a is not void!" will be printed since an object has been assigned to the variable 'a':
a:POINT := #POINT(3,5); if void(a) then #OUT + "a is void!" else #OUT + "a is not void!" end; |
Note that the above test will not work in the same way for some of the built-in classes such as integers and booleans[2].
[2] The void test returns true for all integers with a value of 0 and booleans with a value of false. In general, the void test is not useful for immutable classes.
Each Sather variable and object has an associated type. The type of the object indicates the class that was used to create the object. In the following example, both 'a' and 'b' have the type POINT, indicating that they are associated with instances of the POINT class.
a:POINT := #POINT(2,3); b:POINT := #POINT(4,5); |
In this example, the type of the variable 'a' is the same as the type of the object to which it is assigned. This is always the case with the reference classes we have seen so far.
When we introduce abstract classes (unnamedlink), we will see that some Sather variables can hold objects of many different types. In this case, it is useful to distinguish between the type of the variable (called the declared type) and the type of the object that it holds (called the actual type or the concrete type).
A fundamental feature of object oriented programming languages is that they permit an object to hide certain features which are for internal use only. Attributes may be completely hidden by marking them private. Routines may likewise be marked private, meaning that they cannot be accessed outside the original class. Attributes can also be hidden so that they can be read but not modified from outside the class, by marking them readonly.
class POINT2 is private attr x:INT; -- x cannot be seen from outside readonly attr y:INT; -- y cannot be changed from outside create(xvalue,yvalue:INT):POINT is res:POINT := new; res.x := xvalue; res.y := yvalue; return res end; end; |
This restricts external access to the attributes in the object
foo is -- some other piece of code a:POINT2 := #POINT2(3,5); -- Create a new POINT2 #OUT + a.y; -- Print out '5' -- Illegal: #OUT + a.x; -- Illegal: a.y := 10; end; |
Privacy is on a per-class basis, rather than on a per-object basis. Thus, an object can access the private features of other objects of the same class. We actually use this fact in the create routine of the class POINT2 above. Assignments to the attributes of res are being done outside the object being returned.