next up previous contents
Next: Differences between subtypes and Up: Types and Subtypes, Classes Previous: Types of variables

Classes and Subclasses

Objects consist of both state and operations. The state is typically represented by a collection of instance variables , while the operations are usually referred to as methods . Classes serve as extensible generators of objects, providing initial values for instance variables and the bodies for methods. For example, the ${\it {Point}}$ class in Figure 4 specifies that point objects that are instances of this class will each include ${\it {x}}$ and ${\it {y}}$ instance variables (which are initialized to 0) and will support methods ${\it {getx}}$, ${\it {gety}}$, and ${\it {move}}$. The references to ${\it {x}}$ and ${\it {y}}$ in the methods refer to the instance variables in the point object receiving the message.


  
Figure 4: A Point class.
\begin{figure}
\begin{verbatim}
class Point
 var
 x := 0: Integer;
 y := 0: Inte...
 ...er)
 begin
 x := x + dx; 
 y := y + dy
 end
end class;\end{verbatim}\end{figure}

In our example language, objects can be created by applying the ${\it {new}}$ primitive to a class. Thus evaluation of ${\it {new\ Point}}$ results in the creation of a new point object. Each invocation of ${\it {new\ Point}}$ results in the creation of a distinct instance of a point object. While a class specifies the form (type) and initial values of instance variables and the meaning of the methods, the values of instance variables of individual objects can be changed by the execution of that object's methods. On the other hand the methods associated with an object are fixed and may not be modified once the object has been created.

We do not confuse a class with the type of objects generated by the class. A class contains implementation information rather than just describing the observable properties an object. We discuss this distinction in more detail in the next subsection.

If ${\it {p}}$ is an expression representing a point, then the expression ${\it {p.move(1,2)}}$ results in the look up and execution of ${\it {p}}$'s ${\it {move}}$ method (with parameters 1 and 2). This is usually referred to in object-oriented languages as sending the message ${\it {move(1,2)}}$ to ${\it {p}}$. In many object-oriented languages instance variables are not visible outside of the methods of an object. In such a language, the use of an expression ${\it {p.x}}$ would result in an error message. While the method call ${\it {p.getx()}}$ would return the current value of ${\it {p}}$'s instance variable, ${\it {x}}$, it is legal because it acts through a method of the class. For simplicity, we adopt Smalltalk's convention that all instance variables are hidden outside of the object, and that all methods are visible.

Subclasses support the incremental modification of code by allowing the programmer to define a new class by inheriting the code of an existing class, while possibly modifying or adding instance variables and methods. For example, suppose there exists a graphic window class supporting moving, resizing, and redrawing windows. It is possible to define a window with scroll bars by inheriting from the original window class and adding instance variables to represent the scroll bar and methods to support their use. It will also be necessary to modify the redrawing method to draw the scroll bars in the correct positions.

The ability to incrementally modify a class, without disrupting the behavior of existing code, is the source of much of the power and productivity of the object-oriented paradigm. It is thus important that type-checking rules for object-oriented languages preserve this ability.

We present a simpler example in Figure 5 where we define ${\it {Colorpoint}}$ as a subclass of ${\it {Point}}$. We add a new instance variable, ${\it {color}}$, and methods to access and set the color. To make the example more interesting, we also modify the ${\it {move}}$ method so that a newly moved colorpoint is set to be red. The expression ${\it {
super.move(dx,dy)}}$ in ${\it {Colorpoint}}$'s ${\it {move}}$ method indicates that the method body of ${\it {move}}$ from the superclass should be executed before the assignment of ${\it {red}}$ to the ${\it {color}}$ instance variable. The specification in the header of the subclass declaration that ${\it {move}}$ is to be modified is not strictly necessary, but is a useful safeguard to ensure that an existing method is not accidentally modified.


  
Figure 5: A Colorpoint subclass
\begin{figure}
\begin{verbatim}
class Colorpoint inherits Point modifying move
 ...
 ...begin
 super.move(dx,dy); color := red
 end
end class;\end{verbatim}\end{figure}

Before finishing our discussion of classes and subclasses we need to introduce one more construct. During the execution of a method, it is sometimes necessary to refer to the object executing that method. For example, in a class defining doubly-linked nodes, a method for attaching a new node to the right of an existing node might consist of code for inserting the new node to the right, followed by code sending a message to the new node asking it to attach the existing node to its left. (See the code for the method ${\it {attachRight}}$ in the doubly-linked node class in section 5.) In order to be able to express this, most object-oriented languages include a name for the object executing the method. Existing languages use terms like ${\it {
self}}$ (Object Pascal and Smalltalk), ${\it {this}}$ (C++ and Java), and ${\it {Current}}$ (Eiffel). We will use the term ${\it {
self}}$ in this paper.

In most object-oriented languages, unqualified references to a method ${\it {m}}$ inside a method body is treated as an abbreviation for ${\it {self.m}}$. For clarity we will not adopt that convention and will write out the message sends in full.

On a related note it is important to understand that, in general, the methods of an object can be mutually interdependent. That is, any method of an object can invoke any other method (including itself) during execution of its body by sending an appropriate message to self. In particular, modifying one method of a class in a subclass may have an impact on other methods which use it. This (potential) dependence of each method on the other methods of the class will have an important impact on type checking the methods in a subclass, as any change to the type of one method may affect the types of expressions in the bodies of other methods.


next up previous contents
Next: Differences between subtypes and Up: Types and Subtypes, Classes Previous: Types of variables
Kim Bruce
10/28/1998