Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Wildcard в Java
Wildcard (подстановочный знак) в Java — это символ ?, используемый при работе с generics (обобщённые типы) для создания гибких типизированных конструкций. Wildcard позволяет работать с неизвестным типом параметра шаблона.
Основные типы wildcards
1. Unbounded Wildcard (?)
Представляет любой тип. Используется когда тип параметра не важен.
public class WildcardExample {
// Метод принимает List любого типа
public static void printList(List<?> list) {
for (Object obj : list) {
System.out.println(obj);
}
}
public static void main(String[] args) {
List<String> strings = Arrays.asList("A", "B", "C");
List<Integer> integers = Arrays.asList(1, 2, 3);
printList(strings); // Работает
printList(integers); // Работает
}
}
2. Upper Bounded Wildcard (? extends Type)
Ограничивает wildcard сверху — тип должен быть Type или его подклассом. Это covariant подстановка.
public class UpperBoundedExample {
static class Animal {}
static class Dog extends Animal {}
static class Cat extends Animal {}
// Принимает List животных или их подклассов
public static void processAnimals(List<? extends Animal> animals) {
for (Animal animal : animals) {
System.out.println(animal.getClass().getSimpleName());
}
}
public static void main(String[] args) {
List<Dog> dogs = Arrays.asList(new Dog(), new Dog());
List<Cat> cats = Arrays.asList(new Cat(), new Cat());
processAnimals(dogs); // Работает
processAnimals(cats); // Работает
// Но нельзя добавлять элементы:
// dogs.add(new Dog()); // Компиляционная ошибка
}
}
3. Lower Bounded Wildcard (? super Type)
Ограничивает wildcard снизу — тип должен быть Type или его суперклассом. Это contravariant подстановка.
public class LowerBoundedExample {
static class Animal {}
static class Dog extends Animal {}
// Принимает List, которая может содержать Dog или его родителей
public static void addDogs(List<? super Dog> list) {
list.add(new Dog());
list.add(new Dog());
}
public static void main(String[] args) {
List<Dog> dogs = new ArrayList<>();
List<Animal> animals = new ArrayList<>();
addDogs(dogs); // Работает
addDogs(animals); // Работает
}
}
PECS правило (Producer Extends Consumer Super)
При выборе типа wildcard используй мнемонику PECS:
- Producer Extends — если читаешь данные из коллекции, используй
? extends Type - Consumer Super — если пишешь данные в коллекцию, используй
? super Type
public class PECSExample {
// Producer: читаем животных
public static void readAnimals(List<? extends Animal> animals) {
for (Animal animal : animals) {
// Используем как Animal
}
}
// Consumer: добавляем животных
public static void writeAnimals(List<? super Dog> animals) {
animals.add(new Dog());
}
}
Практические примеры
public class PracticalExample {
// Копирование элементов из одной коллекции в другую
public static <T> void copy(
List<? super T> destination,
List<? extends T> source
) {
for (T item : source) {
destination.add(item);
}
}
// Поиск максимума (читаем элементы)
public static <T extends Comparable<? super T>> T findMax(
List<? extends T> list
) {
T max = list.get(0);
for (T item : list) {
if (item.compareTo(max) > 0) {
max = item;
}
}
return max;
}
public static void main(String[] args) {
List<Integer> src = Arrays.asList(1, 2, 3);
List<Number> dst = new ArrayList<>();
copy(dst, src);
System.out.println(dst); // [1, 2, 3]
}
}
Когда использовать wildcards
Используй wildcards в:
- Сигнатурах методов (параметры и возвращаемые значения)
- Переменных на время жизни
Не используй wildcards для:
- Параметров типов классов
- Если нужна гибкость в чтении И записи одновременно
Важные ограничения
// Нельзя использовать wildcard в создании объектов
// List<?> list = new ArrayList<?>(); // Ошибка
// Нельзя использовать wildcard как тип параметра метода
// public void method(? param) {} // Ошибка
Заключение
Wildcards — мощный инструмент для работы с generics, позволяющий писать более гибкий и переиспользуемый код. Понимание PECS правила критично для правильного их применения.