Что такое параметрический полиморфизм (Parametric Polymorphism)?

Ответ

Параметрический полиморфизм — это вид полиморфизма, при котором код (класс, интерфейс, метод) пишется один раз, но может работать с данными разных типов, которые указываются в качестве параметров. В 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);

Ключевые преимущества:

  1. Безопасность типов на этапе компиляции: Компилятор проверяет соответствие типов, предотвращая ClassCastException во время выполнения.
  2. Устранение приведения типов: Код становится чище, не нужны явные (String) box.getContent().
  3. Повторное использование кода: Один обобщенный алгоритм работает для многих типов.

Ограничения (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(); // Достал и сразу знаешь, что это циферка.

В чём, сука, магия-то?

  1. Безопасность, блядь! Всё ловится на компиляции. Никаких сюрпризов в рантайме, когда у тебя ClassCastException вылезает, как чёрт из табакерки, и всё накрывается медным тазом.
  2. Красота! Убрали эти ёбаные приведения типов (String) something. Код чистый, как слеза младенца (ну, почти).
  3. Один раз написал — везде ебёт! Не надо плодить BoxForString, BoxForInteger, BoxForPidorasa. Один класс — и на все случаи жизни.

А ещё можно этого T приструнить, если он слишком разбаловался. Скажем, "Ты можешь быть кем угодно, но только если умеешь сравниваться (Comparable) и сериализоваться (Serializable)".

public <T extends Comparable<T> & Serializable> void sort(List<T> list) { ... }
// Теперь T — не просто какая-то хуйня, а строго воспитанная хуйня с двумя дипломами.

Но есть, конечно, и подвох, ёпта. В Java дженерики — это такая магия стирания типов. На этапе компиляции они есть, все довольны, типобезопасность. А потом бац — и в скомпилированном байткоде от этих <T> и <String> не остаётся нихуя, всё превращается в Object. Компилятор сам за тебя все проверки сделал и касты вставил где надо. Поэтому во время выполнения ты не спросишь у объекта, какой у него дженерик-тип — он уже всё забыл, как Герасим после того, как Муму утопил. Ну, почти всё, есть некоторые хитрые способы через рефлексию подсмотреть, но это уже извращения.

Короче, дженерики — это овердохуища полезная штука, чтобы не выстрелить себе в ногу и писать красивый, переиспользуемый код. Главное — понять эту простую идею с параметром-типом, и всё сразу встанет на свои места.