Consider a parameterized class Stack:
class Stack #(ItemType: type of any)
{
private:
buffer: ItemType[];
stack_size: int;
stack_top_index: int;
public:
function push (new_item: ItemType)
{ if (stack_top_index < stack_size)
buffer[++stack_top_index]:=new_item;
}
function pop ():ItemType
{ if (stack_top_index >=0)
return buffer[stack_top_index--];
return 0;
}
enter (size: int) // constructor
{ if (size<100) size:=100;
buffer := new ItemType[size];
stack_size := size;
stack_top_index := -1;
};
};
The Stack class is parameterized by a class parameter, ItemType, which can be a subclass of any (the root class).
You would think that this might be nothing but a C++ template class or Eiffel's generic class.
It is more than a C++ template class or Eiffel's generic class. It is a real class. That is, you can use it to specify a variable directly:
my_stack: Stack;
What are the gains when Stack is a real class? Dynamic typing. You get an efficient dynamic typing. You can create concrete Stack class (generic instantiation) either at cmopile time or at run-time. When it is created a run-time, the concrete class is defined dynamically. That is what dynamic typing really means.
There are many cases you want generic instantiations done at run-time. For example, you want to create a concrete collection (array, queue, stack, etc.) based on the type of the objects you obtained from a database, from an user input, or even from the internet. You may need a function as shown below:
function create_stack (): Stack
{
stack: Stack;
obj: any = readObjectFromInternet();
size: int = readIntegerFromUserInput();
if (obj=nil) return nil;
stack := Stack#(typeof(obj))(size);
while (obj)
{
inspect ( typeof(obj) )
{ case typeof(stack).ItemType:
stack.push(obj);
break;
default:
// throw it away;
}
obj := readNextObjectFromInternet();
}
return stack;
}
More examples: