В чем отличие между записью "T extends Comparable<T>" и "? T extends Comparable<T>"?

Рейтинг: 2Ответов: 1Опубликовано: 20.02.2023

В чем отличие между T extends Comparable<T> и ? T extends Comparable<T>? И где какая запись применяется?

Ответы

▲ 4Принят

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]