Here is an initial summary of common definitions, based on the discussion so far:
A data-centric entity with its own information and manipulative activities is called an object. Each information item of an object is called an attribute, and each activity it can engage in is called a method. A use of "object.attribute" or of "object.method" is called a message to the object, and in the contest of carrying such a message, an object may be termed an agent.
If each individual object and its attributes and methods had to be individually declared, these ideas would not give programs much expressive power. However, like variables, constants, and procedures, objects with the same structure are said to have a common type.
The type of an object is called its class. The attributes and methods of a class taken collectively are referred to as the members (a common term in other notations) or components (the Modula-2 term) of the class.
NOTE: In notations other than Modula-2, the term component is usually used of a more or less self-contained routine or collection of routines, such as a module, rather than of an entity constituting part of a class.
An entity that is of a class type is said to be an instance or a member of that class.
Thus, objects are not created alone, but as entities belonging to a class‹usually declared in a fashion somewhat as in the pseudocode:
Class Account = attributes balance : real methods procedure credit (amount : real) procedure debit (amount : real) begin initialization balance <= 0.0 end Account
In practice, the actual syntax of a class declaration may loosely resemble that of a module or a record, but in most object notations, there are several differences from either syntax.
The generation of a specific object (that is, an instance) from some class is called instantiation.
When an object of this class is generated, the data items can be initialized by some kind of internal code, in the manner shown. One might have something like:
var bankAccount : Account;
and then, later,
Create (bankAccount);
where Create is a built in function to allow for the generation and initialization of an object before it is used.
As suggested at the end of section 1, object notations almost always allow for information hiding. As with political systems, which either allow everything not prohibited, or prohibit everything not allowed, there are two common approaches to object scope data hiding, depending on the notation rules:
Similarly, object notations usually allow attributes that are variables to be protected in some way, so that clients can read the value but not change it. This eliminates the necessity (recommended for modules) of keeping the value in the implementation, and only putting a procedure to fetch it into the definition. In most object notations, a visible attribute variable can be changed by clients unless it is marked by a reserved word such as protected or readonly. Thus, one might have, on the one hand, something like:
Class Account = protected balance private overdrawn attributes balance : real overdrawn : boolean methods procedure credit (amount : real) procedure debit (amount : real) begin initialization balance <= 0.0 overdrawn <= false end Account
or, on the other hand, (the Modula-2 hand) something like:
Class Account = reveal credit, debit, Readonly balance attributes balance : real overdrawn : boolean methods procedure credit (amount : real) procedure debit (amount : real) begin initialization balance <= 0.0 overdrawn <= false end Account
In either of these renditions, the method components credit and debit are available for use by objects in the surrounding scope. Of the attributes, balance can be read, but not written to, and overdrawn is kept private. It should be noted, in accord with the preceding discussion, that object notations can achieve these effects in a variety of ways. The discussion is deliberately being kept general at this point, and can apply to any such notation, but the definition below is used in the OOM-2 standard.
An entity whose value is not permitted to be changed is said to be immutable.
The distinction between a value and a reference to a value been made earlier in the text in connection with first value and variable parameters; second the name of a data store and its address (or a pointer).. In brief;
If an identifier denotes the name of the data item itself, it is the name of a value. If it denotes a means of access to a value then it is a reference.
In many object notations, an object name denotes a reference rather than the actual value of the data. Thus, if obj1 and obj2 are both objects, then an assignment like
obj1 := obj2
using value semantics would take the data value in obj2 and copy it to the location of obj2. However, if reference semantics underlie the object notation (the usual), it is the reference that is copied, not the value. Since an object, as with a record or an array, could be rather large, this is probably a good choice for language designers. It has the consequence, however that such objects, like the entities accessed by pointers, can only be created dynamically, not statically.
However, as in the case of pointers, such semantics does raise the issues of dangling references and garbage. It is all very well to do things this way, but what if a series of operations leaves some object with no reference at all (garbage)? After all, if objects are declared and initialized dynamically, that sounds a lot like there is memory allocation and deallocation going on, at least behind the scenes, even if not out in the open as with pointers. Indeed, some object notations require that their object entities be explicitly destroyed by the program so that the memory can be reclaimed, and programs written with such semantics face the very real possibility that if the programmer is not careful with references, the available memory will become exhausted.
However, nearly all implementations of object notation have some form of garbage collection to scour the program memory space for objects that have no current valid references, and collect their memory. That is, an object with no references can be automatically destroyed by the garbage collection routines. It may be possible to invoke these with an explicit procedure call, but often they simply lurk in the background and the program is unaware of them.
Objects that are tracked by a garbage collector and cannot be destroyed manually by the program code are said to be traced. Objects that are subject to manual destruction by explicit code and so are not tracked by a garbage collector are said to be untraced.
In addition, it is worthwhile to distinguish between program units containing one or the other of these two kinds of object:
A program unit (such as a Modula-2 module) that contains only traced objects is called safeguarded. One that contains even one untraced object is called unsafeguarded.
Of course, in some object notations, this distinction will not have to be made, because there will only be one kind of object. Indeed, if language designers (or ISO committees) are dealing with a language that has no pointers, it is easy to declare that all objects are to be traced (essentially what happens in Java). Not so in Modula-2, which has both‹partly because the base language already had pointers and so had the possibility of dynamic allocation and deallocation already.
There are echoes here of comments on loop control variables and whether they can be threatened or not. In some languages, they can be reassigned inside the FOR loop they control; in Modula-2, they cannot be threatened in any way. It should be clear that if objects are to be traced, they have to be protected from attempts to manually dispose of them. For instance, taking the address of a traced value (or casting a traced object reference into a pointer type) constitutes an obvious threat, and is one of the things that would have to be forbidden.