Staram się zrozumieć, jak działa wariancja w Javie.Wariancja typu Java, konsument rodzaju generycznego
W poniższym przykładzie definiuję funkcję test
, która zajmuje Consumer
. Funkcja jest zdefiniowana bez kontrawariancji, więc oczekiwałbym, że Consumer<Object>
nie jest podtypem Consumer<Pair<Animal, Animal>>
. Jednak kod kompiluje i test akceptuje lambda Variance:::superAction
.
Czego mi brakuje?
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 @ komentarzu Thielo za odniesienie superAction
jest odcukrzona do NOT Consumer<Pair<Animal, Animal>>
Consumer<Object>
.
prawidłowy rodzaj dać metodę test
jest coś takiego:
void test(Consumer<? super Pair<? extends Animal, ? extends Animal>>)
Ten typ pozwoli nam przejść Consumer<Object>
do test
, a także pozwalają nam nazywać konsumentowi argumentów jak Pair<Dog, Dog>
zamiast po prostu Pair<Animal, Animal>
.
Jako kolejne pytanie, z tym zaktualizowanym typem do testu, nie będzie już akceptować odwołania do metody, takiego jak void exactMatchAction<Pair<Animal, Animal>>
, tylko void exactMatchAction<Pair<? extends Animal, ? extends Animal>>
. Dlaczego to?
Brak ostrzeżeń, o ile wiem. – asp
Nie wiesz, jak to się dzieje, ale ma sens. Konsument obiektów może również spożywać pary. Pojawi się błąd, jeśli zmienisz ten parametr, aby napisać, String, prawda? – Thilo
Naprawdę, nie wiem. Ale domyślam się, że ma to związek z obsługą '@ FunctionalInterface'. Prawdopodobnie nie dbają o parametry typu samego interfejsu, tylko o to, w jaki sposób są one przywoływane w metodzie. Tak więc metoda 'Object -> void' może prawdopodobnie służyć jako' Pair <> -> void', ponieważ jeśli może pochłonąć * dowolny obiekt *, to oczywiście może pochłonąć parę. – ryachza