Я изо всех сил пытаюсь понять, как дисперсия работает на Java.Ошибка типа Java, потребитель родового типа
В следующем примере я определяю функцию test
, которая принимает Consumer
. Функция определена без контравариантности, поэтому я ожидаю, что Consumer<Object>
не является подтипом Consumer<Pair<Animal, Animal>>
. Тем не менее, код компилируется, и тест принимает лямбда Variance:::superAction
.
Что мне не хватает?
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
import java.util.function.Consumer;
public class Variance {
public static void main(String[] args) {
test(Variance::exactMatchAction);
test(Variance::superAction);
}
private static void exactMatchAction(Pair<Animal, Animal> pair) {
System.out.println(pair.getLeft().getClass().getName());
}
private static void superAction(Object obj) {
System.out.println(obj.getClass().getName());
}
private static void test(Consumer<Pair<Animal, Animal>> action) {
action.accept(ImmutablePair.of(new Animal(), new Animal()));
action.accept(ImmutablePair.of(new Dog(), new Dog()));
}
static class Animal { }
static class Dog extends Animal { }
}
Edit: Per @ комментарий Thielo эталонный superAction
является обессахаренным к Consumer<Pair<Animal, Animal>>
НЕ с Consumer<Object>
.
правильный тип, чтобы дать метод test
что-то вроде:
void test(Consumer<? super Pair<? extends Animal, ? extends Animal>>)
Этот тип позволит пропускать Consumer<Object>
к test
, а также позволяют назвать потребителя с аргументами как Pair<Dog, Dog>
вместо того, чтобы просто Pair<Animal, Animal>
.
В качестве последующего вопроса, с этим обновленным типом для тестирования, он больше не будет принимать ссылку на метод, например void exactMatchAction<Pair<Animal, Animal>>
, только void exactMatchAction<Pair<? extends Animal, ? extends Animal>>
. Почему это?
Предупреждений, насколько я могу судить. – asp
Не знаете, как это реализовано, но это имеет смысл. Потребитель объектов может также потреблять пары. Вы получите сообщение об ошибке, если вы измените этот параметр, чтобы сказать, строка? – Thilo
Воистину, я не знаю. Но я предполагаю, что это связано с тем, как обрабатывается '@ FunctionalInterface'. Вероятно, это не волнует параметры типа самого интерфейса, только то, как они упоминаются в методе. Таким образом, метод 'Object -> void', вероятно, может использоваться как' Pair <> -> void', так как если он может потреблять * любой объект *, то, конечно, внутри него может потребляться пара. – ryachza