Outline
Other Material
The time, the basic idea is to develop code by leave certain key types unspecified, to be filled in later.
In many ways this is like a parameter that is filled with may different values. Here, however, the parameters are types, and not values.
Generics are used both with functions and with classes.
The following illustrates a simple template function in C++, and its use.
template <class T> T max(T left, T right) { if (left < right) return right; return left; } int a = max(3, 27); double d = max(3.14, 2.75); // see how types differ
class Fraction { public: Fraction (int top, int bottom) { t = top; b = bottom; } int numerator() { return t; } int denominator() { return b; } bool operator < (Fraction & right) { return t * right.b < right.t * b; } private: int t, b; }; Fraction x(3, 4); Fraction y(7, 8); Fraction z = max(x, y);
While template functions are useful, it is more common to use templates with classes.
template <class T> class Box { public: Box (T initial) : value(initial) { } T getValue() { return value; } setValue (T newValue) { value = newValue; } private: T value; }; Box<int> iBox(7); cout << iBox.getValue(); 7 iBox.setValue(12); cout << iBox.getValue(); 12
Notice how the programmer filled in the template argument when creating a new variable.
iBox.setValue(3.1415); // ERROR - invalid type Box <double> dBox(2.7); cout << dBox.getValue(); 2.7 dBox.setValue(3.1415); cout << dBox.getValue(); 3.1415 iBox = dBox; // ERROR - mismatched types
In the next chapter we will see how generics are used to create collection classes.
Some languages (Eiffel, others) allow the programmer to place a type on the template argument:
class Hash_Table [ H -> Hashable ] ...
The restriction says that the argument (here named H) can only be a subclass of Hashable.
This feature allows the compiler to do stronger type checking, and produce more meaningful error messages.
Remember the class Box. Suppose a class Person has subclasses BoyChild and GirlChild. What is the relationship between Box[Person] and Box[BoyChild]?
Unfortunately, runs into problems with the principle of substitution.
Assume Box[BoyChild] is a subclass of Box[Person], would make the following legal:
Box[Person] aBox = new Box[BoyChild]; Person aGirl = new GirlChild; aBox.set(aGirl);
A similar argument can be made for the reverse.
Can make a similar argument for arrays:
BoyChild [ ] boys = new BoyChild[10]; Person [ ] people = boys; // copy or pointer semantics? GirlChild sally = new GirlChild; people[1] = sally;
If pointer semantics are used for the array assignment then this can produce type errors. Java allows the assignment, but uses a run-time check to catch the last assignment error!
Other languages make the array assignment illegal.