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); // ОК, это строка
String s2 = (String) list.get(1); // БУМ! ClassCastException

Компилятор молчит, а в рантайме вы получаете ClassCastException. Это как коробка с надписью «яблоки», в которой лежат чашка, банан и ёжик.

Почему это плохо?

  • Ошибки проявляются только во время выполнения.
  • Путаются типы: приходится вручную приводить объекты к нужному типу (cast).
  • Код менее читаемый и более опасный.

Решение — дженерики (обобщения)

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.

Diamond-оператор <>

С 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
Задача
JAVA 25 SELF, 26 уровень, 4 лекция
Недоступна
Каталог моей цифровой библиотеки 📚✍️
Каталог моей цифровой библиотеки 📚✍️
1
Задача
JAVA 25 SELF, 26 уровень, 4 лекция
Недоступна
Волшебные универсальные сундуки 📦✨
Волшебные универсальные сундуки 📦✨
1
Опрос
Коллекции и generics, 26 уровень, 4 лекция
Недоступен
Коллекции и generics
Коллекции и generics
Комментарии (2)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Alpha Уровень 33
29 октября 2025
Для понимания примера с классом Box<T>: class Box<T> - здесь generic <T>, это параметр типа, который говорит нам, что класс будет работать с каким-то типом, который я укажу позже (при создании объекта - Box<String>). В итоге, таким способом мы можем получить универсальный класс, который будет подстраиваться под конкретный тип.
FDX Уровень 47
21 октября 2025
Добавлю про могут быть не только дженерик классы, но и дженерик методы

class Test{ //обычный класс
    public <T> T doSomething(T value){ //тут первая <Т> это объявление типа
        . . .
        return T;
    }
}
такие сигнатуры начинают появляться далее

public static <K, V> Collector<Pair<K, V>, ?, Map<K, List<V>>> toMultiMap()