Узагальнення в Java
Узагальнення в Java (англ. generics) — це можливість узагальненого програмування, що була додана у мову програмування Java в 2004 році як частина стандартної платформи J2SE 5.0. Узагальнення дають можливість створювати типи або методи таким чином, щоб вони могли оперувати різноманітними типами даних, при цьому на етапі компіляції для типів забезпечується відповідний механізм безпеки[1]. Так, наприклад, метод може приймати параметри типу String або Integer і повертати різноманітні типи без реалізації програмістом кількох різних методів. Після появи узагальнень, ряд класів платформи Java були перероблені під їх використання. Так узагальнення реалізовані в колекціях класів Java (Java Collections Framework), які можуть одночасно зберігати та оперувати різноманітними типами даних. До їх появи для досягнення згаданих цілей програміст використовував надкласи тих типів з якими необхідно працювати та коли це було необхідно здійснювалося перетворення типів, проте при такому підході легко допуститися цілого ряду помилок, які виявляються лише під час виконання програми.
Мотивація
Наступний блок Java коду демонструє проблему, що виникає, коли узагальнення не застосовуються. Спочатку створюється список: оголошується ArrayList
, методи якого працюють з даними типу Object, який є суперкласом для усіх класів Java. Тобто ArrayList може працювати з будь-яким типом даних. Далі додаємо рядок тексту типу String
до списку. І зрештою, пробуємо одержати доданий рядок привівши його до типу Integer
.
List v = new ArrayList();
v.add("test");
Integer i = (Integer)v.get(0); // Помилка під час виконання програми
Помилки під час компіляції ми не отримуємо. Проте ми отримуємо повідомлення про виняткову ситуацію під час виконання програми (java.lang.ClassCastException
) у третьому рядку. Даний тип проблеми можна усунути застосувавши узагальнення, наперед передбачивши, які типи можна поміщати в ArrayList.
З використанням узагальнень, вищенаведений код може бути переписаний таким чином:
List<String> v = new ArrayList<String>();
v.add("test");
Integer i = v.get(0); // (type error) Помилка під час компіляції
Параметр типу String
в кутових дужках оголошує, що ArrayList
призначений для String
. З узагальненнями, непотрібне перетворення типів в третьому рядку до будь-якого типу, результатом v.get(0)
буде String
, компілятор тепер знає, який тип повинен бути на виході і більш краще прослідкує за правильністю використання результату методу. Оскільки в програмі ми намагаємось отримати результат у змінну типу Integer, то ми отримаємо помилку під час компіляції програми. Звичайно, що це лише спрощений приклад із застосування узагальнень, але доволі показовий.
Відповідні класи, звичайно ж, повинні бути спроектовані відповідним чином. Ось невеликий витяг з опису інтерфейсів List
та Iterator
в пакеті java.util:
public interface List<E> {
void add(E x);
Iterator<E> iterator();
}
public interface Iterator<E> {
E next();
boolean hasNext();
}
Визначення узагальненого класу
Ось приклад узагальненого класу:
public class Entry<K, V> {
private final K key;
private final V value;
public Entry(K k,V v) {
key = k;
value = v;
}
public K getKey() {
return key;
}
public V getValue() {
return value;
}
public String toString() {
return "(" + key + ", " + value + ")";
}
}
Зазначте, що даний приклад лише для ілюстрації. Кращу реалізацію класу можна знайти тут: stackoverflow.com discussion.
Даний узагальнений клас може бути використаний наступним чином:
Entry<String, String> grade440 = new Entry<String, String>("mike", "A");
Entry<String, Integer> marks440 = new Entry<String, Integer>("mike", 100);
System.out.println("grade: " + grade440);
System.out.println("marks: " + marks440);
Джерела
- Мова програмування Java(англ.)