JavaRush /Курси /JAVA 25 SELF /Generics: навіщо вони потрібні та базовий синтаксис

Generics: навіщо вони потрібні та базовий синтаксис

JAVA 25 SELF
Рівень 26 , Лекція 4
Відкрита

1. Проблема «сирих» колекцій (raw types)

Ненадовго зануримося в історію. До Java 5 усі колекції були «всеїдними». Вони зберігали обʼєкти типу Object, і компілятор не перевіряв, що саме ви туди кладете. Хочете покласти рядок? Будь ласка. Число? Чому ні. Кота? Теж можна.

// Приклад "сирих" колекцій (raw types), Java до 5-ї версії
List list = new ArrayList();
list.add("Привіт");
list.add(42);
list.add(new Object());

Проблема виявлялася під час «діставання» та використання значення:

String s = (String) list.get(0); // OK, це рядок
String s2 = (String) list.get(1); // БУМ! ClassCastException

Компілятор мовчить, а під час виконання ви отримуєте ClassCastException. Це як коробка з написом «яблука», у якій лежать чашка, банан і їжак.

Чому це погано?

  • Помилки виявляються лише під час виконання.
  • Плутаються типи: доводиться вручну кастувати обʼєкти до потрібного типу (cast).
  • Код менш читабельний і небезпечніший.

Рішення — generics (узагальнення)

Generics (узагальнення) — механізм, який дає змогу створювати класи, інтерфейси та методи з параметрами типу. Тобто ви кажете колекції: «Зберігай лише рядки», а компілятор суворо стежить за цим.

List<String> words = new ArrayList<>();
words.add("Привіт");
words.add("Світ");
// words.add(42); // Помилка компіляції! Не можна додати int до List<String>

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

Головна ідея generics:
Забезпечити типобезпеку колекцій (і не лише), щоб помилки ловилися на етапі компіляції, а не під час виконання.

2. Синтаксис generics: як це виглядає в коді

Зазначення типу в кутових дужках

Коли створюєте колекцію, зазначайте тип елементів у <>:

List<String> names = new ArrayList<>();
names.add("Аліса");
names.add("Боб");
// names.add(123); // Помилка: не можна додати число до списку рядків

String first = names.get(0); // Кастування не потрібне!

Класика:

  • List<String> — список рядків
  • List<Integer> — список цілих чисел
  • Set<Double> — множина дійсних чисел
  • Map<String, Integer> — ключ String, значення Integer

Чому не можна писати просто List?

Можна, але ви втрачаєте всі переваги generics, і компілятор попередить:

List list = new ArrayList(); // raw type — не рекомендується!
list.add("Hello");
list.add(7.5);
String s = (String) list.get(1); // Привіт, ClassCastException!

Сучасний Java‑код завжди використовує generics.

Діамантовий оператор <>

Починаючи з Java 7, можна не вказувати тип праворуч, якщо він зрозумілий з контексту:

List<String> list = new ArrayList<>(); // Компілятор сам зрозуміє, що тут <String>

3. Generics для різних колекцій

Приклади для List, Set, Map

List<Integer> numbers = new ArrayList<>();
numbers.add(10);
numbers.add(20);

Set<String> uniqueNames = new HashSet<>();
uniqueNames.add("Аліса");
uniqueNames.add("Боб");

Map<String, Integer> ages = new HashMap<>();
ages.put("Аліса", 23);
ages.put("Боб", 31);

Приклад із власним класом

class Student {
    String name;
    int age;
    // ...
}

List<Student> students = new ArrayList<>();
students.add(new Student());

4. Корисні нюанси

Переваги generics

Типобезпека. Компілятор стежить, щоб до колекції потрапляли лише елементи потрібного типу.

Немає потреби у кастуванні. Раніше: String s = (String) list.get(0);. Тепер: String s = list.get(0);.

Код читабельніший і надійніший. Менше сюрпризів під час виконання.

Обмеження generics

Неможливо використовувати примітивні типи. Узагальнення працюють лише з обʼєктами, а не з примітивами (int, double, boolean). Використовуйте класи-обгортки: Integer, Double, Boolean.

List<Integer> numbers = new ArrayList<>();
numbers.add(10); // int автоматично перетворюється на Integer (autoboxing)

Коротко про стирання типів (type erasure)

У Java generics реалізовані через механізм стирання типів: після компіляції інформація про параметри типу стирається, і під час виконання JVM не знає, що це був List<String>, а не просто List. Це зроблено заради зворотної сумісності.

Наслідок: не можна перевіряти параметр типу через instanceof із конкретним аргументом типу.

List<String> list = new ArrayList<>();
// if (list instanceof List<String>) { ... } // Помилка компіляції!

Спроба додати елемент іншого типу — помилка компіляції

List<String> words = new ArrayList<>();
words.add("Hello");
// words.add(123); // Помилка компіляції: incompatible types: int cannot be converted to String

Map<String, Integer> map = new HashMap<>();
map.put("Кіт", 5);
// map.put(3, "Слон"); // Помилка: ключ має бути String, значення — Integer

І це прекрасно: помилки ловляться на етапі компіляції.

Не лише колекції

Generics можна використовувати у власних класах і методах. Наприклад, універсальна «Коробка»:

class Box<T> {
    private T value;

    public void set(T value) { this.value = value; }
    public T get() { return value; }
}

Box<String> stringBox = new Box<>();
stringBox.set("Привіт");
System.out.println(stringBox.get());

Box<Integer> intBox = new Box<>();
intBox.set(42);
System.out.println(intBox.get());

У колекціях generics — стандарт, але ви зустрінете їх і в інших місцях, наприклад, у Stream API та Optional.

5. Типові помилки під час роботи з generics

Помилка № 1: Використання «сирих» колекцій. Запис виду List list = new ArrayList(); позбавляє типобезпеки. Завжди зазначайте параметри типу, наприклад List<String>.

Помилка № 2: Спроба використовувати примітиви. Не можна написати List<int>, використовуйте List<Integer>.

Помилка № 3: Ручне кастування під час читання з колекції. Якщо ви використовуєте generics, кастування на кшталт (String) list.get(i) не потрібне. Якщо доводиться — десь порушили типи.

Помилка № 4: Очікування, що параметри типів доступні під час виконання. Через стирання типів не можна перевіряти їх через instanceof на кшталт List<String>.

Помилка № 5: Змішування різних типів в одній колекції. Якщо оголошено List<String>, не намагайтеся додати Integer — компілятор не пропустить, і це добре.

1
Опитування
Колекції і generics, рівень 26, лекція 4
Недоступний
Колекції і generics
Колекції і generics
Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ