Параметризованные методы и конструкторы
Generic-метод объявляет собственные параметры типа перед возвращаемым типом. Они не обязаны совпадать с параметрами класса.
public static <T> T first(List<? extends T> values) {
if (values.isEmpty()) {
throw new NoSuchElementException();
}
return values.get(0);
}
String firstName = first(List.of("Ada", "Linus"));Компилятор обычно выводит T из аргументов и целевого типа. Его можно указать явно: Util.<String>first(names).
Bounds
public static <T extends Number & Comparable<? super T>>
T max(List<? extends T> values) {
return values.stream()
.max(Comparator.naturalOrder())
.orElseThrow();
}Сначала указывается не более одного class bound, затем интерфейсы. Ограничение определяет операции, доступные внутри метода.
Generic constructor
Конструктор может иметь собственный type parameter даже у не-generic класса:
final class Box {
private final String text;
<T> Box(T value, Function<? super T, String> formatter) {
this.text = formatter.apply(value);
}
}
Box box = new Box(42, Object::toString);Запись <T> стоит перед именем конструктора. Это параметр одного вызова, он не становится параметром типа Box.
Ограничения erasure
- нельзя
new T(),T.class,new T[10]; - нельзя перегрузить методы, чьи параметры после erasure совпадают;
- static-метод не видит type parameter класса, но может объявить собственный;
- primitive нельзя использовать как type argument напрямую.
На собеседовании
Где пишется <T> generic-метода? Перед возвращаемым типом.
Чем <T> отличается от <?>? T связывает несколько мест одним известным компилятору типом; wildcard описывает неизвестный тип.
Может ли обычный класс иметь generic-конструктор? Да.
Почему нельзя new T()? После erasure нет runtime-информации о конкретном конструкторе T.
Связано: Обобщения generics, Обобщения и коллекции.