← Назад к вопросам

Что такое Wildcard в Generics?

2.0 Middle🔥 191 комментариев
#Основы Java

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Wildcard в Generics

Wildcard (подстановочный символ) — это механизм в Java, который позволяет работать с параметризованными типами более гибко. Обозначается символом `?` (вопросительный знак) и используется для создания переменных типов, способных принимать различные конкретные типы.

Три основные формы Wildcard

1. Unbounded Wildcard (?)

Допускает любой тип. Используется, когда тип параметра не важен:

public void printList(List<?> list) {
    for (Object item : list) {
        System.out.println(item);
    }
}

Можно передать List<String>, List<Integer>, List<Object> — любой список.

2. Upper Bounded Wildcard (? extends Type)

Ограничивает тип сверху. Параметр должен быть подклассом указанного типа:

public double sumNumbers(List<? extends Number> numbers) {
    double sum = 0;
    for (Number num : numbers) {
        sum += num.doubleValue();
    }
    return sum;
}

Анализ:

  • Можно передать List<Integer>, List<Double>, List<BigDecimal>
  • Не можно передать List<String>
  • Можно только читать элементы (безопасно, так как знаем, что это Number)
  • Нельзя добавлять элементы (кроме null), так как конкретный тип неизвестен

3. Lower Bounded Wildcard (? super Type)

Ограничивает тип снизу. Параметр должен быть суперклассом указанного типа:

public void addNumbers(List<? super Integer> list) {
    list.add(10);
    list.add(20);
}

Анализ:

  • Можно передать List<Integer>, List<Number>, List<Object>
  • Можно добавлять Integer (и его подклассы)
  • При чтении получаем Object, так как конкретный тип сверху неизвестен

Принцип PECS (Producer Extends, Consumer Super)

Это золотое правило для выбора вида wildcard:

  • extends — используй, если структура данных производит элементы (только чтение)
  • super — используй, если структура данных потребляет элементы (только запись)
// Producer: читаем из List
public void readData(List<? extends Number> source) {
    Number value = source.get(0); // ✓ работает
}

// Consumer: пишем в List
public void writeData(List<? super Integer> dest) {
    dest.add(42); // ✓ работает
}

Практические примеры

Копирование коллекций:

public static <T> void copy(List<? super T> dest, List<? extends T> src) {
    for (T item : src) {
        dest.add(item);
    }
}

Здесь:

  • src — producer (читаем из неё)
  • dest — consumer (пишем в неё)

Отличие от Type Parameters

// Type parameter — конкретный тип на весь метод
public <T> void method(List<T> list) {
    T item = list.get(0);
}

// Wildcard — гибкий, но неизвестный тип
public void method(List<?> list) {
    Object item = list.get(0); // только Object
}

Ограничения Wildcard

  • Нельзя использовать wildcard в конструкторах
  • Нельзя создавать массивы параметризованных типов с wildcard
  • Wildcard не может использоваться в type bound одного wildcard

Заключение

Wildcard — это мощный инструмент для написания гибких и безопасных API, которые работают с различными типами данных. Правильное использование extends и super делает код более универсальным и соответствующим принципам SOLID.