Java Generics: compareTo и "capture # 1-of?"

Ниже приводится сообщение об ошибке:

public static List<Comparable<?>> merge(Set<List<Comparable<?>>> lists) {
    List<Comparable<?>> result = new LinkedList<Comparable<?>>();
    HashBiMap<List<Comparable<?>>, Integer> location = HashBiMap.create();

    int totalSize;
    for (List<Comparable<?>> l : lists) {
        location.put(l, 0);
        totalSize += l.size();
    }

    boolean first;
    List<Comparable<?>> lowest; //the list with the lowest item to add
    int index;

    while (result.size() < totalSize) {
        first = true;

        for (List<Comparable<?>> l : lists) {
            if (! l.isEmpty()) {
                if (first) {
                    lowest = l;
                }
                else if (l.get(location.get(l)).compareTo(lowest.get(location.get(lowest))) <= 0) { //error here
                    lowest = l;
                }
            }
        }
        index = location.get(lowest);
        result.add(lowest.get(index));
        lowest.remove(index);
    }
    return result;
}

Ошибка:

The method compareTo(capture#1-of ?) in the type Comparable<capture#1-of ?> is not applicable for the arguments (Comparable<capture#2-of ?>)

Что здесь происходит? Я сделал тип всего Comparable, чтобы я мог вызвать .compareTo и отсортировать этот список. Я неправильно использую дженерики?

Ответы

Ответ 1

List<?> означает "Список чего угодно", поэтому два объекта с этим типом не совпадают: может быть список String, а другой - <<22 > . Очевидно, что это не то же самое.

List<T> означает "Список чего-либо, но если вы снова видите T, то тот же T".

Вы должны сообщить компилятору, когда имеете в виду один и тот же тип в разных местах. Попробуйте:

public static <T extends Comparable<? super T>> List<T> merge(Set<List<T>> lists) {
    List<T> result = new LinkedList<T>();
    HashBiMap<List<T>, Integer> location = HashBiMap.create();

[EDIT]. Что означает <T extends Comparable<? super T>> List<T>? Первая часть определяет тип T со следующими свойствами: он должен реализовать интерфейс Comparable<? super T> (или Comparable<X>, где X также определяется в терминах T).

? super T означает, что тип, поддерживаемый Comparable, должен быть T или один из его супер типов.

Представьте себе на мгновение это наследование: Double extends Integer extends Number. Это неверно на Java, но представьте, что Double - это всего лишь Integer плюс доля. В этом случае a Comparable, который работает для Number, также работает для Integer и Double, так как оба получаются из Number. Таким образом, Comparable<Number> удовлетворяет части super для T, являющейся Number, Integer или Double.

Пока каждый из этих типов поддерживает интерфейс Comparable, они также удовлетворяют первой части декларации. Это означает, что вы можете передать Number для T, и полученный код будет работать, если в списках присутствуют экземпляры Integer и Double. Если вы Integer для T, вы все равно можете использовать Double, но Number невозможно, потому что он больше не удовлетворяет T extends Comparable (часть super все равно будет работать).

Следующий шаг - понять, что выражение между static и List просто объявляет свойства типа T, которые используются позже в коде. Таким образом, вам не нужно повторять эту длинную декларацию снова и снова. Это часть поведения метода (например, public), а не часть фактического кода.