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 class in Figure
4 specifies that point objects that are
instances of this class will each include
and
instance variables (which are initialized to 0) and will support methods
,
, and
.
The references to
and
in the methods refer to the instance
variables in the point object receiving the message.
In our example language, objects can be created by applying the
primitive to a class. Thus evaluation of
results in the
creation of a new point object. Each
invocation of
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 is an expression representing a point, then the expression
results in the look up and execution of
's
method (with parameters 1 and 2). This is usually referred
to in object-oriented languages as sending the message
to
. 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
would result in an error message.
While the method call
would return the current value of
's instance variable,
, 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
as a subclass of
. We add a new instance
variable,
, and methods to access and set the color. To
make the example more interesting, we also modify the
method so
that a newly moved colorpoint is set to be red. The expression
in
's
method indicates that the
method body of
from the superclass should be executed before the
assignment of
to the
instance variable. The
specification in the header of the subclass declaration that
is to be modified is not strictly necessary, but is a useful safeguard to
ensure that an existing method is not accidentally modified.
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 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
(Object Pascal and Smalltalk),
(C++ and Java), and
(Eiffel). We will use the term
in this paper.
In most object-oriented languages, unqualified references to a method
inside a method body is treated as an abbreviation for
. 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.