Является ли использование extends в дженериках Java хорошей практикой?

Ответ

Да, это хорошая практика, когда требуется ограничить типы-параметры для повышения безопасности типов. Ключевое слово extends в дженериках задает верхнюю границу (upper bound).

Зачем это нужно:

  1. Безопасность типов: Гарантирует, что параметр типа будет определенного класса или его подкласса.
  2. Доступ к методам: Позволяет обращаться к методам класса-границы внутри обобщенного кода.

Пример класса с ограничением:

// T может быть только Number или его подклассом (Integer, Double, etc.)
public class NumericContainer<T extends Number> {
    private T value;

    public NumericContainer(T value) { this.value = value; }

    public double getSquare() {
        // Можем вызвать метод Number.doubleValue()
        return value.doubleValue() * value.doubleValue();
    }
}

// Использование
NumericContainer<Integer> intBox = new NumericContainer<>(5); // OK
NumericContainer<Double> doubleBox = new NumericContainer<>(3.14); // OK
// NumericContainer<String> stringBox = ... // Ошибка компиляции! String не extends Number

Пример метода с ограничением:

// Метод работает только с массивами Comparable объектов
public static <T extends Comparable<T>> T findMax(T[] array) {
    T max = array[0];
    for (T item : array) {
        if (item.compareTo(max) > 0) max = item;
    }
    return max;
}

Когда НЕ использовать extends: Если ваш класс или метод должен работать с любым типом (Object), то ограничение излишне. Используйте extends только когда ограничение добавляет ценность или является необходимым для логики.

Ответ 18+ 🔞

А, ну это ж классика, блядь! Дженерики и их верхние границы, ёпта! Ситуация, когда ты говоришь: «Слушай, чувак, я тут коробочку сделал, но она не для всякой хуйни, а только для вот этих вот, конкретных, блядь, штук». Это как на складе написать: «Только для инструментов», а какой-то мудак пытается засунуть туда свой бутерброд. Не, братан, не прокатит, компилятор тебе ебальник об асфальт размажет.

Зачем это, нахуй, вообще надо?

  1. Чтобы не обосраться с типами. Ты же не хочешь, чтобы в твой математический калькулятор кто-то засунул строку "пиздец"? Вот и я не хочу. Ограничение extends — это такой охранник на входе, который проверяет пропуск.
  2. Чтобы можно было что-то делать с содержимым. Если ты знаешь, что внутри только Number, то можешь спокойно вызывать .doubleValue() или .intValue(). А если там Object — сиди и гадай, что это: число, строка или, блядь, экземпляр класса ТвояХуйня.

Смотри, как это выглядит в коде, сука:

// Объявляем: T может быть только Number или его детишками (Integer, Double... вся эта числовая шобла).
public class NumericContainer<T extends Number> {
    private T value;

    public NumericContainer(T value) { this.value = value; }

    public double getSquare() {
        // А вот тут уже можно! Мы же уверены, что value — это Number. Дёргаем метод.
        return value.doubleValue() * value.doubleValue();
    }
}

// Работаем
NumericContainer<Integer> intBox = new NumericContainer<>(5); // Всё чики-пуки
NumericContainer<Double> doubleBox = new NumericContainer<>(3.14); // Тоже ок
// NumericContainer<String> stringBox = new NumericContainer<>("ты чё, больной?"); // А вот тут, блядь, компилятор скажет: «Иди нахуй, String — не Number». И будет прав, пидарас шерстяной.

Или вот метод, чтобы найти максимум:

// Говорим: T должен уметь сравнивать сам с собой (быть Comparable). Иначе как мы найдём максимум, а? Гаданием на кофейной гуще?
public static <T extends Comparable<T>> T findMax(T[] array) {
    T max = array[0];
    for (T item : array) {
        if (item.compareTo(max) > 0) max = item;
    }
    return max;
}

А когда это НЕ НАДО, блядь? Да когда твоя задумка — это просто ящик для всего подряд, чёрная дыра, куда можно пихнуть что угодно, от числа до экземпляра класса ТвояСтараяТапочка. Тогда юзай просто <T> и не парь себе мозг. Ограничения — они для тех, у кого есть план, а не «авось пронесёт».