Шаблоны
Ранее рассмотренные классы ( ArrayList, Queue, Stack ) могут поддерживать коллекции произвольных типов — благодаря тому, что любой класс является производным (либо наследует его) класса object. Именно поэтому и можно организовать коллекцию объектов производного типа. Для стека и очереди это всего лишь экземпляры класса object.
Например, Stack сохраняет свои данные в массиве object, и два его метода, Push и Pop, используют object для приема и возвращения данных:
public class Stack{ object[] items; int count; public void Push(object item) {...} public object Pop() {...} }
В этом заключается гибкость и универсальность классов коллекций, и в этом же заключаются основные проблемы, связанные с использованием коллекций.
В коллекции можно разместить объекты произвольного типа. После извлечения объекта из коллекции (например, методом Pop ) полученное значение (ссылка) должна быть явно приведена к соответствующему типу:
Class XXX { ::::: } Stack stack = new Stack(); stack.Push(new XXX()); XXX c = (XXX)stack.Pop();
Если значение типа-значения (например, int ) передается в метод Push, оно автоматически упаковывается. При извлечении значения оно должно быть распаковано с явным приведением типа:
Stack stack = new Stack(); stack.Push(3); int i = (int)stack.Pop();
Такие операции упаковки/распаковки увеличивают непроизводительные издержки, поскольку приводят к динамическим перераспределениям памяти и динамическим проверкам типов.
Следующая проблема заключается в невозможности задания типа данных, помещаемых в коллекцию. Объект – представитель класса XXX может быть помещен в стек и после извлечения преобразован в другой тип:
Stack stack = new Stack(); stack.Push(new XXX()); string s = (string)stack.Pop();
Приведенный здесь код не вызывает возражений транслятора, хотя и является примером некорректного использования класса Stack.
Проблема проявится лишь во время выполнения кода, о чем станет известно после генерации исключения InvalidCastException.
Таким образом шаблоны позволяют избежать вышеперечисленных трудностей.