В чем отличие между записью "T extends Comparable<T>" и "? T extends Comparable<T>"?
В чем отличие между T extends Comparable<T>
и ? T extends Comparable<T>
? И где какая запись применяется?
В чем отличие между T extends Comparable<T>
и ? T extends Comparable<T>
? И где какая запись применяется?
T extends Comparable<T>
Описывает переменную типа, ограниченную сверху при помощи extends
, то есть,
некий обобщённый класс T
должен реализовать интерфейс Comparable<T>
, т.е. объекты такого класса поддерживают естественный порядок и их можно сравнивать и упорядочивать при помощи метода int compareTo(T that)
.
Может использоваться для обычных обобщённых классов
public class Foo<T extends Comparable<T>> {
private T field;
//геттер/сеттер/конструктор
}
т.е. класс Foo
может создаваться не для любых классов T
, а только реализующих Comparable
типа Integer
, String
, LocalDate
, и т.п.:
Foo<Integer> fooInt = new Foo<>(123); // ok
Foo<Random> fooRand = new Foo<>(new Random()); // ошибка, Random не является Comparable
// Type parameter 'java.util.Random' is not within its bound; should implement 'java.lang.Comparable<java.util.Random>'
Соответственно, массивы/списки/стримы таких объектов можно сортировать, для них можно искать минимум/максимум и т.д.:
Доп.информация:
? extends Comparable
Такая запись называется wildcard или подстановкой с верхней границей (upper-bounded).
С её помощью можно организовать ковариантность, т.е. List<Integer>
является подтипом List<Comparable>
. List<? extends Comparable>
может содержать объекты, которые являются Comparable
или наследуются от Comparable
.
Это можно применять для того, чтобы коллекции классов, реализующих Comparable
, присвоить коллекцию объектов с конкретными реализациями:
List<Integer> ints = Arrays.asList(4, 2, 3);
List<String> strs = Arrays.asList("111", "xxxx", "aaaa");
List<LocalDate> dats = Arrays.asList(LocalDate.now(), LocalDate.of(2020, 11, 1), LocalDate.of(2025, 5, 12));
List<? extends Comparable> comps = ints;
comps = strs;
comps = dats;
или же передать такую коллекцию в некий метод, например, для сортировки:
public static void sortAndPrint(List<? extends Comparable> list) {
Collections.sort(list);
System.out.println("sorted: " + list);
}
sortAndPrint(ints); // sorted: [2, 3, 4]
sortAndPrint(strs); // sorted: [111, aaaa, xxxx]
sortAndPrint(dats); // sorted: [2020-11-01, 2023-02-20, 2025-05-12]
? super Comparable
Такая запись называется подстановкой с нижней границей (lower-bounded).
С её помощью можно организовать контравариантность, т.е. List<Comparable>
является подтипом List<? super Integer>
.
Также можно использовать для присваивания или передачи в функции:
List<Comparable> comps2 = Arrays.asList(269, 123, 351);
List<? super Integer> ints2 = comps2;
// Collections.sort(ints2); // Ошибка!
// reason: no instance(s) of type variable(s) T exist so that capture of ? super Integer conforms to Comparable<? super T>
Collections.sort(comps2);
System.out.println(ints2); // [123, 269, 351]
Коллекция с типом ? super T
может использоваться только в качестве приёмника данных, например, для копирования списков существует метод:
Collections::public static <T> void copy(List<? super T> dest, List<? extends T> src)
:
Collections.copy(ints2, ints);
System.out.println(comps2); // [2, 3, 4]
System.out.println(ints2); // [2, 3, 4]