Genericity

We got a very short introduction to generic classes when we were looking at the formal generic part of class structure in 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 LINKABLE, 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 , the producer could build such a class. It might have a query item which would return the thing from the list to which a cursor currently points. It might have a command put which would enter some new thing into the list.

What would be the type of item? And what would be the type of the argument to put?

If the producer wants the class to handle all kinds of things, then the answer must be class ANY, the class from which all others inherit. 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 ...

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: my_cats: LIST_OF_THINGS -- A list of my cats

Then you could add individual instances to the list: fluffy, twinkie: CAT ... my_cats.put (fluffy) my_cats.put (twinkie)

One problem with this type of list is that the type system will not help you keep from doing something pathological like: 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)

Another problem is that to do any CAT things with an item in the list, you must reattach it to a CAT entity. The following is invalid. my_cats.item.purr -- Is invalid

This is because "item" is type ANY and although it may be currently attached to an instance of CAT, the static typing system cannot guarantee that. So you must use an object test as we saw in the polymorphism example in Inheritance . ... if attached my_cats.item as some_cat then some_cat.purr end

You can see that this type of list has its drawbacks. Of course you could build a LIST_OF_CATS class in which item and the argument for put would be of type CAT. This would let you purr 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 LIST_OF_THINGS class might become a class called LIST which is a list of items of type G. In class LIST we would declare item as type G, as well as the argument to put. 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 ...

We could declare feature my_cats as a LIST of items of type CAT. By doing so we are providing CAT as an "actual generic parameter" in the declaration. Then we are free to treat the features of LIST as if the class name CAT had been substituted for every occurrence of the formal generic parameter G. 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

The following would no longer be valid: my_cats: LIST [CAT] -- A list of my cats thor: PSYCHOTIC_HYDROPHOBIC_CAT_HATING_DOG ... my_cats.put (thor) -- Is invalid

Constrained Genericity

The generic class LIST illustrated above is perfectly useful for making typed lists of any type of object. The features of the LIST 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 SORTED_LIST. A SORTED_LIST 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 SORTED_LIST 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 SORTED_LIST were defined like we did LIST class SORTED_LIST [G]

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 SORTED_LIST, we would add to the formal generic part as follows. class SORTED_LIST [G -> COMPARABLE]

You may remember from Inheritance that if we make instances of a class comparable with each other, then we make the class inherit from COMPARABLE and effect the feature "<".

Here, constrained genericity does two things for us.

  • First, it states that any candidate for substitution for G must conform to class COMPARABLE. Typically this means it must inherit from COMPARABLE.
  • Second, it allows, within the features of SORTED_LIST, the features of COMPARABLE to be applied to any item which has a type of G.
cached: 12/21/2024 10:43:33.000 AM