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
Комментарии (4)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Iskander Уровень 46
2 марта 2026
Замечание к последнему вопросу ОПРОСА - Цикл for с конца: Перебор массива от последнего элемента к первому (for (int i = list.size() - 1; i >= 0; i--)) также позволяет удалять элементы, не нарушая индексы оставшихся
Gans Electro Уровень 28
14 июля 2026
Не все коллекций перебираются циклом, по индексу. Там уже нужен итератор. Там ведь не массивы.
Alpha Уровень 42
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()