2015-09-06 14 views
6

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?

+0

Nessun avviso per quanto posso dire. – asp

+0

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

+0

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

risposta

0

espressioni di riferimento Metodo (come il tuo Variance::superAction) sono poli espressioni (JLS8, 15.13). Il tipo di un'espressione poli può essere influenzato dal tipo di destinazione dell'espressione (JLS8, 15.3), che è il tipo previsto in quel contesto (JLS8, 5), ad esempio Consumer<Pair<Animal, Animal>>, nel tuo caso.

I dettagli sono definiti in JLS8, 15.13.2. L'idea di base è che esiste una gestione speciale per tipi di interfaccia funzionali come Consumer. In particolare, il tipo di metodo deve essere solo congruente per il tipo di funzione (che è Pair<Animal, Animal> -> void - nota che Consumer è sparito dal tipo considerazione qui), che è soddisfatto da "identif [ying] una singola dichiarazione di compilazione corrispondente a il riferimento "(e avendo void come tipo di ritorno). Qui, la nozione di "identificazione" di una dichiarazione risale al 15.12.2 e sostanzialmente descrive il processo di risoluzione del sovraccarico del metodo. In altre parole, la lingua assume ora i parametri di funzione previsti per Consumer<Pair<Animal, Animal>>.accept() (ad esempio, Pair<Animal, Animal>) e controlla se il riferimento al metodo può essere chiamato con quello (ciò risolve l'overload nel caso in cui esistano più metodi statici con lo stesso nome).

Problemi correlati