Generics. Шаблони в Java

При створенні колекції можна вказати тип об'єктів, які будуть зберігатися в ній. Приклад:

Надрукує "str1".

Рядок Vector <String> vect; говорить про те, що вектор буде містити тільки рядки. Ось чому у рядку String str = vect.get(0); можна обійтися без приведення типів. При створенні об'єкта вектора вказуємо параметр String: vect = new Vector < String>();.

Головна мета шаблону - type-safety, тобто правильне співвідношення типів.

Формальні параметри

У попередньому прикладі однозначно був вказаний параметр вектора. Є випадки, де зручніше використовувати формальний параметр, подібно до формальних аргументів функцій. Приклад:

Створений параметризований клас FormalVector <E>. E - формальний параметр. При створенні екземпляра класу замість E вказуємо конкретний тип. Простий приклад:

Надрукує "str1".

В класі StringValues створюється екземпляр класу FormalVector <String>. Замість формального параметру E вказуємо String. Тепер клас FormalVector буде зберігати і працювати до типу String. Можна створити екземпляр класу FormalVector з іншим параметром, наприклад, Integer: FormalVector < Integer> vect = new FormalVector < Integer>(). Це означає, що FormalVector < Integer> буде працювати з типом Integer.

Усі примірники параметризованного класу становлять один тип, незалежно від фактичного значення параметрів.

Вгору

Шаблони і підтипи

Важливе правило: якщо клас A є нащадок класу B, то це не означає, що aClass <A> є нащадок aClass <B>.

Методи з шаблонами

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

void aMethod(Vector <E> vect);

або так:

void aMethod(Vector <?> vect);

Рекомендується використовувати невідомий параметр <?> у випадках, коли від нього не залежать інші аргументи та повертає значення. Якщо така залежність існує, то слід використовувати формальний параметр < E>.

Можливо використовувати разом невідомий параметр <?> і формальний параметр <E> :

pablic <E> void aMethod(Vector <E> vect, List <? extends E> list);

тут аргумент list має обмежений невідомий параметр <? extends E>, але є зв'язок між двома аргументами - це формальний параметр < E>.

Вгору

Невідомий параметр

Замість формального параметра можна вказати знак питання, тобто невідомий параметр. Змінимо клас StringValues(див. вище):

Надрукує "str1".

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

private void printValues(FormalVector <?> vect)

Знак питання замість якогось типу говорить про те, що тут може виявитися будь-який тип, т. е. FormalVector <?> - це супертип для будь-якого конкретного типу, наприклад, для FormalVector <String>. Звернемо увагу на перший рядок методу:

Object str = vect.getVal(0);

тут str має тип Object. Це дозволяє безпечно отримувати елементи колекції методом getVal, адже будь-які об'єкти є тип Object.

Невідомий параметр може застосовуватися і при оголошенні полів:

private Vector <?> aVector;

Вгору

Обмежений невідомий параметр

Обмежений невідомий параметр вказує, що замість нього можна підставити не будь-який тип, а тільки тип, успадкований від певного класу. Приклад:

Vector <? extends aClass>

тут на місце невідомого параметра ? можна поставити aClass або його спадкоємців, нехай навіть непрямих. aClass в цьому випадку називається верхньою межею невідомого параметра. Інший приклад:

Vector <? super aClass>

Параметр виду <? super aClass> називається нижня межа. Фактичний параметр повинен бути батьківським по відношенню до aClass, або повинен бути самим aClass.

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

public class ClassA {}

його спадкоємця ClassB

public class ClassB extends ClassA {}

ClassC, як спадкоємця від ClassB:

public class ClassC extends ClassB {}

Чому класи порожні? У цьому прикладі важливо побачити відносини успадкування між класами, а не вміст цих класів.

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

static void addObject(Vector <? extends ClassA> vect) {}

У методі addObject маємо обмежений невідомий параметр <? extends ClassA>, який говорить нам, що аргументом може бути будь-який вектор, що містить об'єкти класу ClassA або його спадкоємців. А такими і є наші три порожніх класу: ClassA, ClassB, ClassC.

В коді методу main створимо об'єкт класу ClassA

ClassA ca = new ClassA();

вектор, що містить об'єкти класу ClassA

Vector < ClassA> vectA = new Vector <ClassA>();

додамо ca до vectA

vectA.add(ca);

Такий вектор відповідає визначенню аргумент методу addObject, до якого ми і звертаємося:

addObject(vectA);

Для класів ClassB, ClassC дії аналогічні. Ось повний код класу MainClass:

Якщо метод addObject оголосити так:

static void addObject(Vector <ClassA> vect) {}

тобто без обмеженого невідомого параметра, то отримаємо повідомлення про помилки в рядках addObject(vectB); і addObject(vectC);(невідповідність типів аргументів). Чому? Дивіться пункт "Шаблони і підтипи". Vector < ClassB> і Vector < ClassC> є підтипи не Vector < ClassA>, а Vector < ? extends ClassA>.

Вгору

Що таке raw type?

Оголошуючи клас-колекцію, ми вказуємо параметр, тобто тип об'єктів, що зберігаються в ньому. Якщо не вказати параметр, то можемо отримати попередження "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 призвести до рядка


Вгору

Що таке безконтрольно conversion?

Це означає, що компілятор не гарантує відповідність типів. Приклад:

Ми не знаємо, що буде зберігатися в aVect, там може виявитися і не рядок, звідси попередження. Виправляємо положення, вказавши параметр для aVect:

Угода по іменам параметрів

Параметричні типи прийнято обзначать T, якщо є кілька параметрів, то для наступних використовують букви, близькі до T, тобто S, U, V і т. д. Елементи колекцій позначають буквою E. Ключі - K, значення - V, числа - N. У всіх випадках параметри позначають великими літерами.