Освоюємо Java/Узагальнення

Матеріал з Вікіпідручника

Узагальнення в Java (англ. generics)— це можливість узагальненого програмування, що була додана у мову програмування Java в 2004 році як частина стандартної платформи J2SE 5.0. Узагальнення дають можливість створювати типи або методи таким чином, щоб вони могли оперувати різноманітними типами даних, при цьому на етапі компіляції для типів забезпечується відповідний механізм безпеки[1]. Так, наприклад, метод може приймати параметри типу String або Integer і повертати різноманітні типи без реалізації програмістом кількох різних методів. Після появи узагальнень, ряд класів платформи Java були перероблені під їх використання. Так узагальнення реалізовані в колекціях класів Java (Java Сollections 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);


(Даний розділ не завершений, потребує доповнення...)

Джерела[ред.]

Потоки вводу-виводу · Паралелізм