Что такое Java Generics

Ответ

Generics в Java — это механизм для создания обобщённых типов, позволяющий писать код, который работает с разными типами данных, сохраняя безопасность типов на этапе компиляции. Они помогают избежать ошибок ClassCastException и уменьшают необходимость в приведении типов.

Пример:

List<String> strings = new ArrayList<>();
strings.add("Hello");
String value = strings.get(0); // Безопасно, не требует приведения типов

Ключевые моменты:

  • Параметризация типов (<T>)
  • Ограничения через extends (например, <T extends Number>)
  • Совместимость с обратной совместимостью (стирание типов)

Ответ 18+ 🔞

Ну ты смотри, придумали же люди! Сидят, значит, в своих пещерах с Java, и думают: "А как бы нам так сделать, чтобы код был универсальный, но при этом компилятор не орал как резаный, когда мы в список строк пытаемся запихнуть Integer?" И родились дженерики, ёпта!

Это, грубо говоря, такие заглушки для типов. Вместо того чтобы писать отдельный класс для String, отдельный для Integer и ещё один для какого-нибудь Cat, ты пишешь один, но с пометкой <T>. И этот T — он как подстановочная карточка, блядь. Хочешь — будет String, хочешь — Integer. Главное — договориться с компилятором заранее.

Вот смотри, раньше было вот это вот пиздецкое веселье:

List strings = new ArrayList(); // Просто список, ёбаный в рот
strings.add("Hello");
String value = (String) strings.get(0); // Обязательно надо было кастовать, а то вылетит ClassCastException и все, пизда.

А теперь, с этими вашими дженериками, красота:

List<String> strings = new ArrayList<>();
strings.add("Hello");
String value = strings.get(0); // Всё! Никакого приведения! Компилятор уже знает, что там внутри только строки. Удивление пиздец, как удобно!

Попробуй теперь сделать strings.add(123); — компилятор тебе сразу: "Э, сабака, сука! Я ж тебе сказал, что тут только String! Иди нахуй со своим интеджером". И не скомпилирует. Это и есть безопасность типов, ёбана вошь. Ошибка ловится ещё до запуска, а не в рантайме, когда уже клиенты орут.

А ещё там можно эти самые <T> ограничивать, представляешь? Типа говоришь: "Слушай, компилятор, мой тип T — это не просто любая хуйня с горы, а только то, что extends Number". И всё, внутри метода можно спокойно вызывать .intValue(), потому что ты уверен, что там Integer, Double или какой BigDecimal.

public <T extends Number> void processNumber(T num) {
    int intValue = num.intValue(); // Безопасно, ёпта! Потому что T — гарантированно наследник Number.
}

Самое дикое, что для обратной совместимости всю эту магию потом стирают, блядь. На этапе компиляции они есть, всё проверяется, а в байткоде уже нет — остаются просто Object. Это называется стирание типов. Ну, чтобы старый код, написанный до дженериков, не сломался. Хитро, да? С одной стороны, безопасность, с другой — старый хлам не разваливается. Ёперный театр, в общем.

Короче, вещь полезная. Сначала мозг выносит, когда пытаешься понять List<? super T>, но потом, когда врубаешься, жить становится проще. Главное — не пытайся впихнуть невпихуемое, компилятор всё видит, он хитрый жопа.