PECS Для генериков в Non Collections - PullRequest
0 голосов
/ 07 февраля 2019

Джошуа Блох придумал PECS, в котором написано правило, когда использовать?расширяет Т а?супер Т. Если вы думаете о PECS с точки зрения структуры коллекций, то это очень просто.Если вы добавляете значения в структуру данных, используйте?супер Т. Если вы читаете из структуры данных, используйте?расширяет Т. например:

public class Collections {  
    public static <T> void copy(List<? super T> dest, List<? extends T> src) {  
        for (int i = 0; i < src.size(); i++)   
            dest.set(i, src.get(i));   
    }   
}

Если я проверяю подпись

public static <T> void sort(List<T> list, Comparator<? super T> c) 

Я вижу, что Comparator использует?супер, так что это должен быть потребитель.Глядя на код, компаратор c используется только для производства чего-либо, потому что его спрашивают о логике сравнения.

С одной стороны, я понимаю, почему это супер, потому что как разработчик я хочу использовать компараторы классаT, а также компараторы суперкласса T, потому что объекты T также относятся к суперклассам типа T. Но когда я пытаюсь мыслить в терминах PECS, я не понимаю.?Если нет, может кто-нибудь объяснить мне, что компаратор потребляет в Collections.sort?

1 Ответ

0 голосов
/ 07 февраля 2019

Ради этого ответа возьмем Comparator в качестве основного, руководящего примера.

Если вы внимательно об этом подумаете, вы увидите, что Comparator на самом деле получает два аргумента типа T и возвращает результат их сравнения (представленный int).Другими словами, он потребляет два экземпляра типа T, а создает и int значение.Таким образом, согласно правилу PECS, это потребитель из T, следовательно, использование ? super T.

В более общем случае, вы должны рассмотреть производитель и потребитель с точки зрения основного типа в отношении типов каждого из его общих параметров.Если какой-то Comparator тип потребляет объекты типа T, правило PECS гласит, что пользователи такого Comparator<T> могут использовать его для сравнения объектов, тип которых является подтипом T.

В качестве конкретного примера, если у вас уже есть логика для сравнения двух общих Number экземпляров (независимо от того, каков их конкретный тип на самом деле), вы можете использовать его, то есть для сравнения Double экземпляров, потому что удваиваютсяв конце концов, числа.

Рассмотрим следующий компаратор:

Comparator<Number> c = Comparator.comparingInt(Number::intValue);

Здесь компаратор c сравнивает Number экземпляров ( любое число), принимая во вниманиеучитывайте только их неотъемлемую часть.

Если у вас есть следующий список Double экземпляров:

List<Double> doubles = Arrays.asList(2.2, 2.1, 7.3, 0.2, 8.4, 9.5, 3.8);

и следующий sort метод:

static <T> void sort(List<T> list, Comparator<T> c) {
    list.sort(c);
}

(Обратите внимание на отсутствие подстановочного знака ? super T в аргументе Comparator).

Затем, если вы хотите отсортировать список List<Double> doubles, подпись вышеуказанного метода sort потребует от васпередать бетон Comparator<Double>.Но что, если вы хотите использовать ранее определенный компаратор c для сортировки List<Double> doubles?

Поскольку тип этого компаратора равен Comparator<Number>, а тип списка doubles равен List<Double>, следующий код вызовет ошибку компиляции:

sort(doubles, c);

К счастью, как Comparator является потребителем типа элементов, которые он сравнивает, вы можете изменить сигнатуру метода sort на:

static <T> void sort(List<T> list, Comparator<? super T> c) {
    list.sort(c);
}

И теперь этот код будет компилироваться:

sort(doubles, c);
...