Принцип generic типов Java

188
10 февраля 2020, 06:00

Пытаюсь написать параметризуемый метод, принимающий параметризованные типы:

    public static <T> void findMinMax(
                Stream<? extends T> stream,
                Comparator<? super T> comparator,
                BiConsumer<? super T, ? super T> minMaxConsumer
        ) {
            Stream<T> sortedStream = stream.sorted(comparator);
    }

Объявление метода имею из условия задачи. Непонятны две вещи:

  1. Не понимаю почему stream может параметризоваться любым подтипом T, а comparator любым родительским типом T. То есть почему у stream тип Stream<? extends T>, а у comparator - Comparator<? super T>? С какой целью это сделано?

  2. В данный момент при компиляции вылетает ошибка:

error: incompatible types: Stream<CAP#1> cannot be converted to Stream<T>
        Stream<T> sortedStream = stream.sorted(comparator);
                                                  ^
  where T is a type-variable:
    T extends Object declared in method <T>findMinMax(Stream<? extends T>,Comparator<? super T>,BiConsumer<? super T,? super T>)
  where CAP#1 is a fresh type-variable:
    CAP#1 extends T from capture of ? extends T

Не могу понять, что не так. Ведь T является и своим подтипом и своим супер-типом. Почему тогда компилятор говорит о несовместимости типов? Что компилятор подразумевает под CAP#1?

Answer 1

Generics (Обобщенные типы) инвариантны, то есть нельзя присвоить к Stream<Number> ссылку на Stream<Double>. Wildcard (Маска) придает свойство вариантности при взаимодействии с обобщенными типами рассматривая их либо как источники объектов Т (Producer Extends, см. PECS), либо как приёмники Т (Consumer Super, см. PECS).

Wildcard это не Generic, это специальный тип параметризации, его можно использовать только при декларировании переменных или аргументов методов, все Wildcard содержат вопросительный знак - ?.

Инвариантный Generic (Обобщенный тип):

List<T> - List принимает T и его наследников, и возвращает объекты Т.

Ковариантность достигается через Upper Bounded Wildcards ? extends T:

List<? extends T> - List возвращает объекты Т, так как содержит объекты Т либо наследников Т:

List<? extends Number> numbers = new ArrayList<Double>() {{ add(2.5); }};
Number n = numbers.get(0);

Контравариантность достигается через Lower Bounded Wildcards ? super T:

List<? super T> - List принимает объекты T и его наследников:

List<? super Number> objects = new ArrayList<Object>() {{ add(2.5); add("it's fine!"); }}
objects.add((short) 256);

Этот wildcard указывает, что исходный List может содержать что угодно, вплоть до Object, по этой причине метод get(n) вернет Object, но wildcard позволит передать в List объекты с типом Number и его наследников.

Разбор ошибки:

public static <T> void findMinMax(Stream<? extends T> stream) {
    Stream<T> sortedStream = stream.sorted(comparator);
}

Ошибка из-за попытки присвоить Upper Bounded Wildcards к типу Stream.

Почему так сделать нельзя:

List<? extends Number> containsNumbers = new ArrayList<Double>();
containsNumbers.add(new Integer(100)); // Ошибка, wildcard `? extends Number` позволяет только прочитать объекты типа `Number`.
List<Number> numbers = containsNumbers // Ошибка, нельзя присвоить List<? extends Number>
numbers.add(new Integer(200)); // если бы не ошибка выше, то можно было бы добавить объект неподходящего типа
READ ALSO
Заполнить таблицу enum с помощью JPA

Заполнить таблицу enum с помощью JPA

Есть два класса enum и класс содержащий его как атрибут

200
Почему у java нет парсера собственного кода?

Почему у java нет парсера собственного кода?

Сразу скажу, что знаю, есть, Javaparser проект, но он от сторонних разработчиковЯ просто не могу понять если java компилируется в байт-код из исходников,...

209
Чем proxy через BeanPostProcessor отличается от AOP?

Чем proxy через BeanPostProcessor отличается от AOP?

Не могу понять, чем прокси класс который создается, когда я в BeanPostProcessor добавляю какую-нибудь логику после инит метода отличается от того...

220
Не компилируется JavaFX приложение

Не компилируется JavaFX приложение

Импортировал библиотеку, но приложение не компилируетсяКод исходный, я ничего не менял

167