Some of the classes that we will need, particularly in libraries, are container classes, describing data structures made of a number of objects of the same or similar types. Examples of containers include arrays, stacks and lists. The class DEPOSIT_LIST posited in earlier examples describes containers.
It is not hard, with the mechanisms seen so far, to write the class DEPOSIT_LIST , which would include such features as count (query returning the number of deposit objects in the list) and put (command to insert a new deposit object).
Most of the operations, however, would be the same for lists of objects other than deposits. To avoid undue replication of efforts and promote reuse, we need a way to describe generic container classes, which we can use to describe containers containing elements of many different types.
The operations available on an entity such as first and val , whose type is a formal generic parameter, are the operations available on all types: use as source y of an assignment x := y , use as target x of such an assignment (although not for val , which as a formal routine argument is not writable), use in equality comparisons x = y or x /= y , and application of universal features from ANY such as clone , equal and copy .
To use a generic class such as list, a client will provide a type name as actual generic parameter . So instead of relying on a special purpose class DEPOSIT_LIST , the class ACCOUNT could include the declaration
using LIST as a generic class and DEPOSIT as the actual generic parameter. Then all features declared in LIST as working on values of type G will work, when called on the target all_deposits , on values of type DEPOSIT . With the target
Genericity reconciles extendibility and reusability with the static type checking demanded by reliability. A typical error, such as confusing an account and a deposit, will be detected immediately at compile time, since the call all_accounts . extend ( dep ) is invalid for dep declared of type DEPOSIT . What is valid is something like all_accounts . extend ( acc ) for acc of type ACCOUNT . In other approaches, the same effect might require costly run-time checks (as in Java, C# or Smalltalk), with the risk of run-time errors.
This form of genericity is known as unconstrained because the formal generic parameter, G in the example, represents an arbitrary type. You may also want to use types that are guaranteed to have certai operations available. This is known as constrained genericity and will be studied with inheritance.
The creation procedure make , as in create my_array . make ( 1, 50 ) which creates an array with the given index bounds. It is also possible to resize an array through resize , retaining the old elements. In general, the Eiffel method abhors built-in limits, favoring instead structures that resize themselves when needed, either from explicit client request or automatically.
The comment made about INTEGER and other basic classes applies to ARRAY too: Eiffel compilers know about this class, and will be able to process expressions of the form my_array . put ( val , 25 ) and my_array @ 25 in essentially the same way as a C or Fortran array access -- my_array [ 25 ] in C. But it is consistent and practical to let developers treat ARRAY as a class and arrays as objects; many library classes in EiffelBase, for example, inherit from ARRAY . Once again the idea is to get the best of both worlds: the convenience and uniformity of the object-oriented way of thinking; and the efficiency of traditional approaches.
The introduction of genericity brings up a small difference between classes and types. A generic class C is not directly a type since you cannot declare an entity as being of type C : you must use some actual generic parameter T -- itself a type. C [ T ] is indeed a type, but class C by itself is only a type template.
The process of obtaining a type C [ T ] from a general class C is known as a generic derivation ; C [ T ] is a generically derived type . Type T itself is, recursively, either a non-generic class or again a generically derived type D [ U ] for some D and U , as in LIST [ ARRAY [ INTEGER ]] .)
Copyright Interactive Software Engineering, 2001