Sto lottando per capire come funziona la varianza in Java.Variante di tipo Java, consumer di tipo generico
Nell'esempio seguente, definisco una funzione test
che accetta uno Consumer
. La funzione è definita senza controvarianza, quindi mi aspetto che Consumer<Object>
non sia un sottotipo di Consumer<Pair<Animal, Animal>>
. Tuttavia, il codice viene compilato e il test accetta lambda Variance:::superAction
.
Cosa mi manca?
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 @ commento di Thielo, il riferimento è superAction
private degli zuccheri ad un NON Consumer<Pair<Animal, Animal>>
un Consumer<Object>
.
Il tipo corretto di dare il metodo test
è qualcosa di simile:
void test(Consumer<? super Pair<? extends Animal, ? extends Animal>>)
Questo tipo ci permetterà di passare un Consumer<Object>
-test
, e ci permettono inoltre di chiamare il consumatore con argomenti come Pair<Dog, Dog>
invece di Pair<Animal, Animal>
.
Come domanda di follow-up, con questo tipo di test aggiornato, non accetterà più un riferimento al metodo come void exactMatchAction<Pair<Animal, Animal>>
, solo void exactMatchAction<Pair<? extends Animal, ? extends Animal>>
. Perchè è questo?
Nessun avviso per quanto posso dire. – asp
Non so come sia implementato, ma ha senso. Un consumatore di oggetti può anche consumare coppie. Si ottiene un errore se si modifica quel parametro per dire, una stringa, giusto? – Thilo
Davvero, non lo so. Ma la mia ipotesi è che questo abbia a che fare con '@ FunctionalInterface'. Probabilmente non si preoccupa dei parametri di tipo dell'interfaccia stessa, solo del modo in cui sono indicati nel metodo. Quindi il metodo 'Object -> void' può probabilmente essere usato come' Pair <> -> void', dal momento che se può consumare * qualsiasi oggetto *, allora ovviamente può consumarne una coppia. – ryachza