Ответ
Параметрический полиморфизм — это вид полиморфизма, при котором код (класс, интерфейс, метод) пишется один раз, но может работать с данными разных типов, которые указываются в качестве параметров. В Java реализуется через дженерики (Generics).
Суть: Создание обобщенных, типобезопасных абстракций.
Пример обобщенного класса Box:
public class Box<T> { // `T` — параметр типа (Type Parameter)
private T content;
public void setContent(T content) { this.content = content; }
public T getContent() { return content; }
}
// Использование с конкретными типами (Type Arguments)
Box<String> stringBox = new Box<>(); // T становится String
stringBox.setContent("Hello");
// stringBox.setContent(123); // ОШИБКА КОМПИЛЯЦИИ: типобезопасность!
Box<Integer> intBox = new Box<>(); // T становится Integer
intBox.setContent(123);
Ключевые преимущества:
- Безопасность типов на этапе компиляции: Компилятор проверяет соответствие типов, предотвращая
ClassCastExceptionво время выполнения. - Устранение приведения типов: Код становится чище, не нужны явные
(String) box.getContent(). - Повторное использование кода: Один обобщенный алгоритм работает для многих типов.
Ограничения (Bounds): Параметр типа можно ограничить.
public <T extends Comparable<T> & Serializable> void sort(List<T> list) { ... }
// T должен быть подтипом и Comparable<T>, и Serializable
Важно: В Java дженерики реализованы через стирание типов (Type Erasure). Информация о параметрах типа удаляется во время компиляции и недоступна во время выполнения (за исключением некоторых случаев через Reflection).
Ответ 18+ 🔞
Слушай, ну вот есть у тебя код, а он, блядь, как тот самый Герасим — здоровый, сильный, но нихуя не говорит, на каком языке. Хочешь строку в него сунь, хочешь циферку — а он молчит, как партизан, и в итоге тебе самому приходится догадываться, что там внутри и не сломается ли всё в пизду в самый ответственный момент.
Так вот, чтобы не быть немым крестьянином в мире программирования, придумали параметрический полиморфизм, или, по-простому, дженерики. Суть проще, чем кажется: ты пишешь код один раз, а он, хитрая жопа, умеет работать с любым типом, который ты ему скормишь. Тип этот ты указываешь как параметр, прямо как аргумент в методе.
Смотри, вот классический пример — коробка. Раньше была коробка Object, куда можно было засунуть и бутерброд, и кирпич, и кота. Доставаешь — охуеваешь, что за хуйня и во что это кастить. Теперь же:
public class Box<T> { // Видишь эту <T>? Это и есть наш параметр, типа "Чё класть будем?"
private T content; // Здесь лежит что-то типа T
public void setContent(T content) { this.content = content; }
public T getContent() { return content; }
}
И теперь, когда мы эту коробку используем, мы ей напрямую в ебало говорим, что в ней будет:
Box<String> stringBox = new Box<>(); // Говорим: "Бля, T — это String, запомни!"
stringBox.setContent("Привет, ёпта!");
// stringBox.setContent(123); // А вот эту хуйню компилятор уже не пропустит! Сразу орёт: "Иди нахуй, тут строки должны быть!"
Box<Integer> intBox = new Box<>(); // А здесь T — уже Integer
intBox.setContent(42); // Всё, красота. Никаких лишних кастов.
Integer value = intBox.getContent(); // Достал и сразу знаешь, что это циферка.
В чём, сука, магия-то?
- Безопасность, блядь! Всё ловится на компиляции. Никаких сюрпризов в рантайме, когда у тебя
ClassCastExceptionвылезает, как чёрт из табакерки, и всё накрывается медным тазом. - Красота! Убрали эти ёбаные приведения типов
(String) something. Код чистый, как слеза младенца (ну, почти). - Один раз написал — везде ебёт! Не надо плодить
BoxForString,BoxForInteger,BoxForPidorasa. Один класс — и на все случаи жизни.
А ещё можно этого T приструнить, если он слишком разбаловался. Скажем, "Ты можешь быть кем угодно, но только если умеешь сравниваться (Comparable) и сериализоваться (Serializable)".
public <T extends Comparable<T> & Serializable> void sort(List<T> list) { ... }
// Теперь T — не просто какая-то хуйня, а строго воспитанная хуйня с двумя дипломами.
Но есть, конечно, и подвох, ёпта. В Java дженерики — это такая магия стирания типов. На этапе компиляции они есть, все довольны, типобезопасность. А потом бац — и в скомпилированном байткоде от этих <T> и <String> не остаётся нихуя, всё превращается в Object. Компилятор сам за тебя все проверки сделал и касты вставил где надо. Поэтому во время выполнения ты не спросишь у объекта, какой у него дженерик-тип — он уже всё забыл, как Герасим после того, как Муму утопил. Ну, почти всё, есть некоторые хитрые способы через рефлексию подсмотреть, но это уже извращения.
Короче, дженерики — это овердохуища полезная штука, чтобы не выстрелить себе в ногу и писать красивый, переиспользуемый код. Главное — понять эту простую идею с параметром-типом, и всё сразу встанет на свои места.