mirror of
https://github.com/EiffelSoftware/eiffel-org.git
synced 2025-12-08 15:52:26 +01:00
git-svn-id: https://svn.eiffel.com/eiffel-org/trunk@2400 abb3cda0-5349-4a8f-a601-0c33ac3a8c38
145 lines
7.4 KiB
Plaintext
145 lines
7.4 KiB
Plaintext
[[Property:title|Genericity]]
|
|
[[Property:weight|4]]
|
|
[[Property:uuid|3a0bfe10-78e7-00eb-d9a0-b977d1fa352a]]
|
|
We got a very short introduction to generic classes when we were looking at the formal generic part of class structure in [[Eiffel Classes|Eiffel Classes]] . That discussion left to the imagination the motivation and benefits for creating generic classes.
|
|
|
|
You will see that most of the generic classes model containers for multiple items and at least one of their formal generic parameters represents the type of items that are to be stored in the container. Some generic classes, like <code>LINKABLE</code>, care for only one instance of the type represented by their formal generic parameter.
|
|
|
|
==Motivation==
|
|
|
|
Imagine that a software producer is planning to build a class which would represent a list of things. Someone might ask "What kinds of things?" To which the producer would reply, "Just things. I want my list to be usable for all kinds of things."
|
|
|
|
Using the idea of polymorphic attachment that we learned in [[Inheritance|Inheritance]] , the producer could build such a class. It might have a query <code>item</code> which would return the thing from the list to which a cursor currently points. It might have a command <code>put</code> which would enter some new thing into the list.
|
|
|
|
What would be the type of <code>item</code>? And what would be the type of the argument to <code>put</code>?
|
|
|
|
If the producer wants the class to handle all kinds of things, then the answer must be class <code>ANY</code>, the class from which all others inherit.
|
|
<code>
|
|
class
|
|
LIST_OF_THINGS
|
|
...
|
|
feature -- Access
|
|
|
|
item: ANY
|
|
-- The thing currently pointed to by cursor
|
|
...
|
|
feature -- Element change
|
|
|
|
put (new_item: ANY)
|
|
-- Add `new_item' at the end of the list
|
|
...
|
|
</code>
|
|
|
|
This will work, but has some definite disadvantages. Suppose you choose to use this class to maintain a list of cats in one of your classes. You might make this declaration:
|
|
<code>
|
|
my_cats: LIST_OF_THINGS
|
|
-- A list of my cats
|
|
</code>
|
|
|
|
Then you could add individual instances to the list:
|
|
<code>
|
|
fluffy, twinkie: CAT
|
|
...
|
|
my_cats.put (fluffy)
|
|
my_cats.put (twinkie)
|
|
</code>
|
|
|
|
One problem with this type of list is that the type system will not help you keep from doing something pathological like:
|
|
<code>
|
|
fluffy, twinkie: CAT
|
|
thor: PSYCHOTIC_HYDROPHOBIC_CAT_HATING_DOG
|
|
-- A very nasty dog
|
|
...
|
|
my_cats.put (fluffy)
|
|
my_cats.put (twinkie)
|
|
my_cats.put (thor)
|
|
</code>
|
|
|
|
Another problem is that to do any <code>CAT</code> things with an item in the list, you must reattach it to a <code>CAT</code> entity. The following is invalid.
|
|
<code>
|
|
my_cats.item.purr -- Is invalid
|
|
</code>
|
|
|
|
This is because "item" is type <code>ANY</code> and although it may be currently attached to an instance of <code>CAT</code>, the static typing system cannot guarantee that. So you must use an object test as we saw in the polymorphism example in [[Inheritance|Inheritance]] .
|
|
<code>
|
|
...
|
|
if attached my_cats.item as some_cat then
|
|
some_cat.purr
|
|
end
|
|
</code>
|
|
|
|
You can see that this type of list has its drawbacks. Of course you could build a <code>LIST_OF_CATS</code> class in which <code>item</code> and the argument for <code>put</code> would be of type <code>CAT</code>. This would let you <code>purr</code> a cat without pulling it out of the list, and it would also prevent you from accidently letting old Thor in with the cats. But, every time you needed a list to hold a different type of object, you have to write a new class.
|
|
|
|
Indeed, this is how things are done in environments without facilities genericity.
|
|
|
|
What we would like to have is a way to produce the text of the list class once. Then only when we make declarations do we add the additional information about the particular types we want allowed in the list.
|
|
|
|
==Basic Genericity==
|
|
|
|
In Eiffel this is accomplished through generic classes. Generic classes are written relative not to a specific class but to a kind of phony class name called a formal generic parameter. With genericity, the <code>LIST_OF_THINGS</code> class might become a class called <code>LIST</code> which is a list of items of type <code>G</code>. In class <code>LIST</code> we would declare <code>item</code> as type G, as well as the argument to <code>put</code>.
|
|
<code>
|
|
class
|
|
LIST [G]
|
|
...
|
|
feature -- Access
|
|
item: G
|
|
-- The item currently pointed to by cursor
|
|
...
|
|
feature -- Element change
|
|
put (new_item: G)
|
|
-- Add `new_item' at the end of the list
|
|
...
|
|
</code>
|
|
|
|
We could declare feature <code>my_cats</code> as a <code>LIST</code> of items of type <code>CAT</code>. By doing so we are providing <code>CAT</code> as an "actual generic parameter" in the declaration. Then we are free to treat the features of <code>LIST</code> as if the class name <code>CAT</code> had been substituted for every occurrence of the formal generic parameter <code>G</code>.
|
|
<code>
|
|
my_cats: LIST [CAT]
|
|
-- A list of my cats
|
|
fluffy, twinkie: CAT
|
|
...
|
|
my_cats.put (fluffy)
|
|
my_cats.put (twinkie)
|
|
...
|
|
my_cats.item.purr -- Valid now
|
|
</code>
|
|
|
|
The following would no longer be valid:
|
|
<code>
|
|
my_cats: LIST [CAT]
|
|
-- A list of my cats
|
|
|
|
thor: PSYCHOTIC_HYDROPHOBIC_CAT_HATING_DOG
|
|
...
|
|
my_cats.put (thor) -- Is invalid
|
|
</code>
|
|
|
|
==Constrained Genericity==
|
|
|
|
The generic class <code>LIST</code> illustrated above is perfectly useful for making typed lists of any type of object. The features of the <code>LIST</code> will not attempt to use the objects in the list in any way. Sometimes though, it is important for a class to be guaranteed more about the nature of the types that can be substituted for its formal generic parameter.
|
|
|
|
Take for example the case of a class called <code>SORTED_LIST</code>. A <code>SORTED_LIST</code> is a list, of course, but it is special in that it acts upon the elements that it holds to keep them in order.
|
|
|
|
A <code>SORTED_LIST</code> needs to be able to order its elements. So, it must be able to apply queries to those elements to determine which should sort high than which. The elements themselves must respond to ordering operations.
|
|
|
|
If <code>SORTED_LIST</code> were defined like we did <code>LIST</code>
|
|
<code>
|
|
class
|
|
SORTED_LIST [G]
|
|
</code>
|
|
|
|
there would be no guarantee that ordering operations, like "<" and ">" could be applied to all types that could be listed. An Eiffel facility called "constrained genericity" will solve this problemfor us. In the case of <code>SORTED_LIST</code>, we would add to the formal generic part as follows.
|
|
<code>
|
|
class
|
|
SORTED_LIST [G -> COMPARABLE]
|
|
</code>
|
|
|
|
You may remember from [[Inheritance|Inheritance]] that if we make instances of a class comparable with each other, then we make the class inherit from <code>COMPARABLE</code> and effect the feature "<".
|
|
|
|
Here, constrained genericity does two things for us.
|
|
* First, it states that any candidate for substitution for <code>G</code> must conform to class <code>COMPARABLE</code>. Typically this means it must inherit from <code>COMPARABLE</code>.
|
|
* Second, it allows, within the features of <code>SORTED_LIST</code>, the features of <code>COMPARABLE</code> to be applied to any item which has a type of <code>G</code>.
|
|
|
|
|
|
|
|
|