Подстановочные типы с нижней границей
? super T для потребителей
«Подстановочные типы с нижней границей» — бесплатный урок Java Academy на CoddyKit. Это урок 2 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Java Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Java Academy содержит 4 уроков всего.
Подстановочный знак с ограничением снизу
? super T — это подстановочный знак с ограничением снизу. Он означает «некоторый неизвестный тип, являющийся T или одним из супертипов T».
List<? super Integer> может ссылаться на List<Integer>, List<Number> или List<Object>.
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<? super Integer> a = new ArrayList<Number>();
List<? super Integer> b = new ArrayList<Object>();
a.add(1);
b.add(2);
System.out.println(a + " " + b);
}
}Запись в потребитель
С помощью ? super Integer можно безопасно добавлять любые значения Integer (или его подтипов).
Каков бы ни был фактический тип списка, это как минимум список некоторого супертипа Integer, поэтому Integer всегда подходит.
import java.util.ArrayList;
import java.util.List;
public class Main {
static void addNumbers(List<? super Integer> dest) {
for (int i = 1; i <= 3; i++) dest.add(i);
}
public static void main(String[] args) {
List<Number> nums = new ArrayList<>();
addNumbers(nums);
System.out.println(nums);
}
}При чтении Вы получаете Object
Компромисс заключается в следующем: при чтении из списка ? super T компилятор знает только, что элементы имеют некоторый неизвестный супертип T.
Единственный безопасный тип для чтения — Object.
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<? super Integer> list = new ArrayList<Number>();
list.add(10);
Object o = list.get(0); // only Object is guaranteed
// Integer i = list.get(0); // would NOT compile
System.out.println(o);
}
}Почему чтение ограничено
List<? super Integer> на самом деле может быть List<Object>, содержащим строки.
Поэтому результатом чтения может быть любой Object. Компилятор не может гарантировать, что это Integer, и потому чтение даёт только Object.
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<Object> raw = new ArrayList<>();
raw.add("a string");
List<? super Integer> view = raw; // legal: Object is a supertype of Integer
view.add(5);
System.out.println(view.get(0).getClass().getSimpleName());
}
}Сценарий использования потребителя
Используйте ? super T, когда метод только потребляет значения, то есть записывает их.
Классический случай — метод, заполняющий целевую коллекцию.
import java.util.ArrayList;
import java.util.List;
public class Main {
static void fill(List<? super String> dest, int count) {
for (int i = 0; i < count; i++) dest.add("item" + i);
}
public static void main(String[] args) {
List<Object> objs = new ArrayList<>();
fill(objs, 3);
System.out.println(objs);
}
}Подтипы тоже можно добавлять
С помощью ? super Number можно добавлять любые значения Number и его подтипов: Integer, Double, Long.
Ограничение снизу гарантирует, что список принимает как минимум Number, поэтому подходят все его подтипы.
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<? super Number> list = new ArrayList<Object>();
list.add(1); // Integer
list.add(2.5); // Double
list.add(3L); // Long
System.out.println(list);
}
}В стиле Collections.addAll
JDK использует ограничения снизу для приёмников. Например, обобщённый метод может помещать элементы в целевую коллекцию.
import java.util.ArrayList;
import java.util.List;
public class Main {
@SafeVarargs
static <T> void addAll(List<? super T> dest, T... items) {
for (T item : items) dest.add(item);
}
public static void main(String[] args) {
List<Object> sink = new ArrayList<>();
addAll(sink, "a", "b", "c");
System.out.println(sink);
}
}Компаратор с ограничением снизу
Практический пример: Comparator<? super T> позволяет сортировке принимать компаратор, написанный для супертипа.
Компаратор, сравнивающий любые Object с помощью toString, может сортировать список строк.
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
public class Main {
public static void main(String[] args) {
Comparator<Object> byString = Comparator.comparing(Object::toString);
List<String> names = new ArrayList<>(List.of("Charlie", "Alice", "Bob"));
names.sort(byString); // List<String>.sort accepts Comparator<? super String>
System.out.println(names);
}
}Ограничения снизу и сверху — зеркальные противоположности
Два подстановочных знака противоположны:
? extends T: чтение T, запись невозможна. Это производитель.? super T: запись T, чтение только как Object. Это потребитель.
Эта симметрия лежит в основе правила PECS, которое Вы изучите далее.
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<? super Integer> consumer = new ArrayList<Number>();
consumer.add(1);
System.out.println("consumer can write: " + consumer);
}
}Копирование между ограничениями
Метод copy одновременно демонстрирует оба подстановочных знака: чтение из ? extends T и запись в ? super T.
import java.util.ArrayList;
import java.util.List;
public class Main {
static <T> void copy(List<? extends T> src, List<? super T> dest) {
for (T t : src) dest.add(t);
}
public static void main(String[] args) {
List<Integer> src = List.of(1, 2, 3);
List<Number> dest = new ArrayList<>();
copy(src, dest);
System.out.println(dest);
}
}Когда не следует использовать подстановочный знак
Если метод и читает, и записывает значения типа T, не используйте подстановочный знак. Используйте точный тип или именованный параметр типа.
Подстановочные знаки предназначены именно для случаев, когда выполняется только чтение или только запись.
import java.util.ArrayList;
import java.util.List;
public class Main {
static <T> void swapFirstTwo(List<T> list) {
T tmp = list.get(0);
list.set(0, list.get(1));
list.set(1, tmp);
}
public static void main(String[] args) {
List<String> list = new ArrayList<>(List.of("a", "b"));
swapFirstTwo(list);
System.out.println(list);
}
}Быстрая проверка
Проверьте, насколько хорошо Вы поняли ограничения снизу.
Итоги
Вы изучили подстановочные знаки с ограничением снизу:
? super Tозначает неизвестный супертип T.- Можно записывать T и его подтипы (это потребитель).
- При чтении возвращается только Object.
- Это точное зеркальное отражение
? extends T.
Далее — правило, объединяющее оба случая: PECS.
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<? super Integer> list = new ArrayList<Number>();
list.add(42);
System.out.println("Lower bound recap: " + list);
}
}Часто задаваемые вопросы
Урок «Подстановочные типы с нижней границей» бесплатный?
Да — полный текст урока «Подстановочные типы с нижней границей» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Java Academy, подпишись на CoddyKit PRO. Курс Java Academy содержит 4 уроков всего.
Чему я научусь в уроке «Подстановочные типы с нижней границей»?
? super T для потребителей Ты практикуешь Java Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Java Academy?
Предыдущий опыт не требуется. Java Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 2 из 4.
Сколько времени занимает урок «Подстановочные типы с нижней границей»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Java Academy?
Да. Каждый урок Java Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Подстановочные типы с верхней границей
- Подстановочные типы с нижней границей
- Принцип PECS
- Подстановочные типы в API