Files
eiffel-org/documentation/current/method/eiffel-tutorial-et/et-genericity-and-arrays.wiki
halw 7020d63f69 Author:halw
Date:2008-10-23T14:51:56.000000Z


git-svn-id: https://svn.eiffel.com/eiffel-org/trunk@95 abb3cda0-5349-4a8f-a601-0c33ac3a8c38
2008-10-23 14:51:56 +00:00

69 lines
7.2 KiB
Plaintext

[[Property:title|7 Genericity and Arrays]]
[[Property:link_title|ET: Genericity and Arrays]]
[[Property:weight|-9]]
[[Property:uuid|7f3bd1d7-357e-031d-9faa-b00594aa9ae0]]
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 <code>DEPOSIT_LIST</code> posited in earlier examples describes containers.
It is not hard, with the mechanisms seen so far, to write the class <code>DEPOSIT_LIST</code>, which would include such features as <code>count</code> (query returning the number of deposit objects in the list) and <code>put</code> (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.
==Making a class generic==
The notation
<code>
class C [G]
... The rest as for any other class declaration ...</code>
introduces a generic class. A name such as <code>G</code> appearing in brackets after the class name is known as a '''formal generic parameter'''; it represents an arbitrary type.
Within the class text, feature declarations can freely use <code>G</code> even though it is not known what type <code>G</code> stands for. Class <code>LIST</code> of EiffelBase, for example, includes features
<code>
first: G
-- Value of first list item
extend (val: G)
-- Add a new item of value val at end of list
...
</code>
The operations available on an entity such as <code>first</code> and <code>val</code>, whose type is a formal generic parameter, are the operations available on all types: use as source <code>y</code> of an assignment <code>x := y</code>, use as target <code>x</code> of such an assignment (although not for <code>val</code>, which as a formal routine argument is not writable), use in equality comparisons <code>x = y</code> or <code>x /= y</code>, and application of universal features from <code>ANY</code> such as <code>twin</code>, <code>is_equal</code> and <code>copy</code>.
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 <code>DEPOSIT_LIST</code>, the class <code>ACCOUNT</code> could include the declaration
<code>all_deposits: LIST [DEPOSIT]</code>
using <code>LIST</code> as a generic class and <code>DEPOSIT</code> as the actual generic parameter. Then all features declared in <code>LIST</code> as working on values of type <code>G</code> will work, when called on the target <code>all_deposits</code>, on values of type <code>DEPOSIT</code>. With the target
<code>all_accounts: LIST [ACCOUNT]</code>
these features would work on values of type <code>ACCOUNT</code>.
{{info|A note of terminology: to avoid confusion, Eiffel always uses the word '''argument''' for routine arguments, reserving '''parameter''' for the generic parameters of classes. }}
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 <code>all_accounts</code>. <code>extend</code> <code>(</code> <code>dep</code> <code>)</code> is invalid for <code>dep</code> declared of type <code>DEPOSIT</code>. What is valid is something like <code>all_accounts</code>. <code>extend</code> <code>(</code> <code>acc</code> <code>)</code> for <code>acc</code> of type <code>ACCOUNT</code>. 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.
{{info|This form of genericity is known as '''unconstrained''' because the formal generic parameter, <code>G</code> in the example, represents an arbitrary type. You may also want to use types that are guaranteed to have certain operations available. This is known as '''constrained''' genericity and will be studied with inheritance. }}
==Arrays==
An example of generic class from the Kernel Library is <code>ARRAY</code> <code>[G]</code>, which describes direct-access arrays. Features include:
* <code>put</code> to replace an element's value, as in <code>my_array.put (val, 25)</code> which replaces by <code>val</code> the value of the array entry at index 25.
* <code>item</code> to access an entry, as in <code>my_array.item (25)</code> yielding the entry at index 25. A synonym is <code>infix</code> <code>"@"</code>, so that you may also write more tersely, for the same result, <code>my_array</code> <code>@</code> <code>25</code> .
* <code>lower</code>, <code>upper</code> and <code>count</code>: queries yielding the bounds and the number of entries.
* The creation procedure <code>make</code>, as in <code>create my_array.make (1, 50)</code> which creates an array with the given index bounds. It is also possible to resize an array through <code>resize</code>, 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 <code>INTEGER</code> and other basic classes applies to <code>ARRAY</code> too: Eiffel compilers know about this class, and will be able to process expressions of the form <code>my_array.put (val, 25)</code> and <code>my_array</code> <code>@</code> <code>25</code> in essentially the same way as a C or Fortran array access -- <code>my_array</code> <code>[25]</code> in C. But it is consistent and practical to let developers treat <code>ARRAY</code> as a class and arrays as objects; many library classes in EiffelBase, for example, inherit from <code>ARRAY</code>. 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.
A similar technique applies to another Kernel Library class, that one not generic: <code>STRING</code>, describing character strings with a rich set of string manipulation features.
==Generic derivation==
The introduction of genericity brings up a small difference between classes and types. A generic class <code>C</code> is not directly a type since you cannot declare an entity as being of type <code>C</code>: you must use some actual generic parameter <code>T</code> -- itself a type. <code>C</code> <code>[T]</code> is indeed a type, but class <code>C</code> by itself is only a type template.
The process of obtaining a type <code>C</code> <code>[T]</code> from a general class <code>C</code> is known as a '''generic derivation'''; <code>C</code> <code>[T]</code> is a '''generically derived type'''. Type <code>T</code> itself is, recursively, either a non-generic class or again a generically derived type <code>D</code> <code>[U]</code> for some <code>D</code> and <code>U</code>, as in <code>LIST</code> <code>[ARRAY [INTEGER]]</code>.)
It remains true, however, that every type is based on a class. The base class of a generically derived type <code>C</code> <code>[T]</code> is <code>C</code>.