Generics. Шаблоны



Поддержка JavaScript отключена

SBP-Program
На главную -> Java &nbsp

&nbsp

При создании коллекции можно указать тип объектов, которые будут храниться в ней. Пример:

Напечатает “str1”.

Строка Vector &lt String&gt vect; говорит о том, что вектор будет содержать только строки. Вот почему в строке String str = vect.get(0); можно обойтись без приведения типов. При создании объекта вектора указываем параметр String: vect = new Vector &lt String&gt();.

Главная цель шаблонов – type-safety, т.е. правильное соотношение типов.

В предыдущем примере однозначно был указан параметр вектора. Есть случаи, где удобнее использовать формальный параметр, подобно формальным аргументам функций. Пример:

Создан параметризованный класс FormalVector &lt E&gt. E – формальный параметр. При создании экземпляра класса вместо E указываем конкретный тип. Простой пример:

Напечатает “str1”.

В классе StringValues создаётся экземпляр класса FormalVector &lt String&gt. Вместо формального параметра E указываем String. Теперь класс FormalVector будет хранить и работать с типом String. Можно создать экземпляр класса FormalVector с другим параметром, например, Integer: FormalVector &lt Integer&gt vect = new FormalVector &lt Integer&gt();. Это значит, что FormalVector &lt Integer&gt будет работать с типом Integer.

Все экземпляры параметризованного класса представляют один тип, независимо от фактического значения параметров.

Важное правило: если класс A есть потомок класса B, то это не означает, что aClass &lt A&gt есть потомок aClass &lt B&gt.

Шаблоны могут применяться и в методах. Типы аргументов можно делать параметрическими. Пример:

void aMethod(Vector &lt E&gt vect);

или так:

void aMethod(Vector &lt ?&gt vect);

Рекомендуется использовать неизвестный параметр &lt ?&gt в случаях, когда от него не зависят другие аргументы и возвращаемое значение. Если такая зависимость имеется, то следует использовать формальный параметр &lt E&gt.

Возможно использовать вместе неизвестный параметр &lt ?&gt и формальный параметр &lt E&gt :

pablic &lt E&gt void aMethod(Vector &lt E&gt vect, List &lt ? extends E&gt list);

здесь аргумент list имеет ограниченный неизвестный параметр &lt ? extends E&gt, но имеется связь между двумя аргументами – это формальный параметр &lt E>.

Вместо формального параметра можно указать знак вопроса, т.е. неизвестный параметр. Изменим класс StringValues(см. выше):

Напечатает “str1”.

Новым для нас является аргумент метода

private void printValues(FormalVector &lt ?&gt vect)

Знак вопроса вместо какого-то типа говорит о том, что здесь может оказаться любой тип, т.е. FormalVector &lt ?&gt — это супертип для любого конкретного типа, например, для FormalVector &lt String&gt. Обратим внимание на первую строку метода:

Object str = vect.getVal(0);

здесь str имеет тип Object. Это позволяет безопасно получать элементы коллекции методом getVal, ведь любые объекты представляют тип Object.

Неизвестный параметр может примениться и при объявлении полей:

private Vector &lt ?&gt aVector;

Ограниченный неизвестный параметр указывает, что вместо него можно подставить не любой тип, а только тип, наследуемый от определённого класса. Пример:

Vector &lt ? extends aClass&gt

здесь на место неизвестного параметра ? можно поставить aClass или его наследников, пусть даже непрямых. aClass в этом случае называется верхней границей неизвестного параметра. Другой пример:

Vector &lt ? super aClass&gt

Параметр вида &lt ? super aClass&gt называется нижняя граница. Фактический параметр должен быть родительским по отношению к aClass, или должен быть самим aClass.

Рассмотрим формальный пример. Создадим три пустых класса: ClassA

public class ClassA{}

его наследника ClassB

public class ClassB extends ClassA{}

ClassC, как наследника от ClassB:

public class ClassC extends ClassB{}

Почему классы пустые? В этом примере важно увидеть отношения наследования между классами, а не содержимое этих классов.

Теперь создадим класс MainClass, а в нём метод

static void addObject(Vector &lt ? extends ClassA&gt vect){}

В методе addObject имеем ограниченный неизвестный параметр &lt ? extends ClassA&gt, который говорит нам, что аргументом может быть любой вектор, содержащий объекты класса ClassA или его наследников. А таковыми и являются наши три пустых класса: ClassA, ClassB, ClassC.

В коде метода main создадим объект класса ClassA

ClassA ca = new ClassA();

вектор, содержащий объекты класса ClassA

Vector &lt ClassA&gt vectA = new Vector &lt ClassA&gt();

добавим ca к vectA

vectA.add(ca);

Такой вектор соответсвует определению аргумента метода addObject, к которому мы и обращаемся:

addObject(vectA);

Для классов ClassB, ClassC действия аналогичные. Вот полный код класса MainClass:

Если метод addObject объявить так:

static void addObject(Vector &lt ClassA&gt vect){}

т.е. без ограниченного неизвестного параметра, то получим сообщения об ошибках в строкахaddObject(vectB); и addObject(vectC);(несоответствие типов аргументов). Почему? Смотрите пункт “Шаблоны и подтипы”. Vector &lt ClassB&gt и Vector &lt ClassC&gt есть подтипы не Vector &lt ClassA&gt, а Vector &lt ? extends ClassA&gt.

Объявляя класс-коллекцию, мы указываем параметр, т.е. тип объектов, хранящихся в нём. Если не указать параметр, то можем получить предупреждение “raw type”. Пример:

Можно объявлять коллекции без указания параметра, это сделано для работы с прежним кодом, когда ещё не было шаблонов.Ещё пример:

В последней строке получаем предупреждение, которое приведёт к ошибке при попытке извлечь строку из vect:

System.out.print((String) vect.get(0));

Сообщение об ошибке: java.lang.ClassCastException: java.lang.Double cannot be cast to java.lang.String.Исправить положение можно так: указать параметр для vect, Double привести к строке

Это значит, что компилятор не гарантирует соответствие типов. Пример:

Мы не знаем, что будет храниться в aVect, там может оказаться и не строка, отсюда предупреждение. Исправляем положение, указав параметр для aVect:

Соглашение по именам параметров

Параметрические типы принято обзначать T, если имеется несколько параметров, то для следующих используют буквы, близкие к T, т.е. S, U, V и т.д. Элементы коллекций обозначают буквой E. Ключи – K, значения — V, числа – N. Во всех случаях параметры обозначают большими буквами.

Программирование

&nbsp
&nbsp
&nbsp