В чем разница между ключевыми словами extends и super в Java Generics (PECS)?

«В чем разница между ключевыми словами extends и super в Java Generics (PECS)?» — вопрос из категории Java Core, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Ключевые слова extends и super в дженериках задают ограничения (bounds) для типов и связаны с принципом PECS (Producer Extends, Consumer Super).

  • ? extends T (Upper Bounded Wildcard - "Производитель")

    • Смысл: Обозначает «некоторый тип, который является T или его подтипом».
    • Гарантия: Из коллекции с таким ограничением можно читать (получать) элементы, и они будут гарантированно приводимы к типу T. Коллекция выступает производителем (Producer).
    • Запрет: Нельзя добавлять элементы (кроме null), так как компилятор не знает точный тип внутри.
      // Метод может читать числа из списка любого подтипа Number (List<Integer>, List<Double>)
      void printNumbers(List<? extends Number> list) {
      for (Number n : list) { // Чтение - OK
          System.out.println(n);
      }
      // list.add(new Integer(10)); // Ошибка компиляции! Добавление - НЕЛЬЗЯ.
      }
  • ? super T (Lower Bounded Wildcard - "Потребитель")

    • Смысл: Обозначает «некоторый тип, который является T или его супертипом».
    • Гарантия: В коллекцию с таким ограничением можно записывать (добавлять) элементы типа T и его подтипов. Коллекция выступает потребителем (Consumer).
    • Запрет: При чтении можно получить только объекты типа Object, так как точный тип неизвестен.
      // Метод может добавлять Integer в список, который содержит Integer или его супертипы (List<Number>, List<Object>)
      void addIntegers(List<? super Integer> list) {
      list.add(new Integer(5)); // Запись - OK
      // Integer i = list.get(0); // Ошибка компиляции! Чтение как Integer - НЕЛЬЗЯ.
      Object obj = list.get(0); // Чтение как Object - OK
      }

PECS (Producer Extends, Consumer Super):

  • Если параметризованный тип производит значения (T), используйте ? extends T.
  • Если параметризованный тип потребляет значения (T), используйте ? super T. Это правило обеспечивает максимальную гибкость и безопасность типов.