2017-02-01 46 views
5

Proszę rozważyć następujący przykład:Co decyduje, który interfejs funkcjonalny utworzyć z lambda?

import java.util.function.Consumer; 

public class Example { 
    public static void main(String[] args) { 
     Example example = new Example(); 

     example.setConsumer(test -> System.out.println("passed string is " + test)); //uses MyConsumer, why ? 
     example.getConsumer().accept("Test 1"); 

     example.setConsumer((MyConsumer<String>)test -> System.out.println("passed string is " + test)); //uses MyConsumer 
     example.getConsumer().accept("Test 2"); 

     example.setConsumer((Consumer<String>)test -> System.out.println("passed string is " + test)); //uses Consumer 
     example.getConsumer().accept("Test 3"); 
    } 

    private Consumer<String> consumer; 

    public Consumer<String> getConsumer() { 
     return consumer; 
    } 

    public void setConsumer(Consumer<String> consumer) { 
     this.consumer = consumer; 
    } 

    public void setConsumer(MyConsumer<String> consumer) { 
     this.consumer = consumer; 
    } 

    @FunctionalInterface 
    public interface MyConsumer<T> extends Consumer<T> { 
     @Override 
     default void accept(T value) { 
      System.out.println("In consumer string: " + value); //example thing to do 
      receive(value); 
     } 

     void receive(T value); 
    } 
} 

Co tu interesuje mnie to pierwsza próba. Dlaczego korzysta z MyConsumer zamiast z Consumer? Co jeśli miałbym więcej różnych Konsumentów o tej samej strukturze lambda, kto ma pierwszeństwo? Dodatkowo obsada, którą wykonuję w teście 2, jest oznaczona jako IDprzez moje IDE. Oznacza to, że lamdba jest najpierw tworzona jako MyConsumer. Dlaczego tak ?

Używam IntelliJ IDEA z javac.

+0

Nie wiem, którego IDE używasz. Myślę, że to JVM, używając typu środowiska wykonawczego obiektu, "decyduje". – duffymo

+3

Myślę, że Java wybiera najbardziej specyficzny typ, który w tym przypadku jest "MyConsumer". Jeśli 'MyConsumer' nie byłby podinterfejsem' Consumer', myślę, że dostałeśby błąd, mówiąc, że jest to połączenie ambiwalentne. – marstran

+7

Wybiera najbardziej szczegółową metodę. Gdyby na przykład 'MyConsumer2 rozszerzył Konsumenta ' i 'setConsumer (MyConsumer2 konsumenta), wówczas pierwsze wywołanie byłoby niejednoznaczne i wystąpiłby błąd czasu kompilacji. Zobacz także [JLS 15.12.2.5] (https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.12.2.5). –

Odpowiedz

6

To według procedury choosing the most specific method jak określono w specyfikacji języka:

Jeśli więcej niż jedna metoda członkiem jest zarówno dostępne i stosowane metody w inwokacji, konieczne jest, aby wybrać jedną, aby zapewnić deskryptor w przypadku wywołania metody w czasie wykonywania. Język programowania Java wykorzystuje regułę, że wybrana jest najbardziej szczegółowa metoda .

...

funkcjonalny typu interfejs S dokładniej niż typu interfejsu funkcjonalnego T do wyrażenia E, jeżeli T nie jest podtypem S, a jedna z poniższych sytuacji (gdzie U1. .. Uk i R1 są typami parametrów i typem powrotu typu funkcji przechwytywania S, a V1 ... Vk i R2 są typami parametrów i typem powrotu typu funkcji T):

  • Jeśli e jest jawnie wpisanym wyrażeniem lambda (§15.27.1), wówczas spełniony jest jeden z następujących warunków:
  • R2 jest nieaktualna.

  • < R1: R2.

  • R1 i R2 są funkcyjnymi typami interfejsów i istnieje co najmniej jedno wyrażenie wyniku, a R1 jest bardziej specyficzny niż R2 dla każdego wyniku wyrażenia e.

    (Wyrażenie wynikiem ekspresji lambda z klocka jest zdefiniowany w §15.27.2; ekspresji wyniku ekspresji lambda z organem wyrażenia jest po prostu sam korpus.)

  • R1 oznacza typ prymitywny, a R2 jest typem referencyjnym i występuje przynajmniej jedno wyrażenie wyniku, a każdy wynik wyrażenia e jest wyrażeniem niezależnym (§15.2) typu pierwotnego.

  • R1 jest typem odniesienia, a R2 jest typem pierwotnym i występuje co najmniej jedna ekspresja wyniku, a każda ekspresja wyniku e jest niezależnym wyrażeniem typu odniesienia lub wyrażenia poli.

  • Jeżeli E jest dokładną metodą ekspresji odniesienia (§15.13.1), a następnie I) dla wszystkich i (1 ≤ i ≤ k), Ui jest taka sama jak Vi, oraz ii) jeden z następujących warunków:
  • R2 jest nieaktualna.

  • < R1: R2.

  • R1 jest typem pierwotnym, R2 jest typem odniesienia, a deklaracja czasu kompilacji dla odwołania do metody ma typ zwracany, który jest typem pierwotnym.

  • R1 jest typem odniesienia, R2 jest typem pierwotnym, a deklaracja czasu kompilacji dla odwołania do metody ma typ zwracany, który jest typem odniesienia.

  • Jeśli e jest wyrażenie w nawiasach, po czym jeden z tych warunków dotyczy rekurencyjnie do zamkniętego wypowiedzi.

  • Jeśli e jest wyrażeniem warunkowym, to dla każdego z drugiego i trzeciego argumentu jeden z tych warunków ma zastosowanie rekursywnie.

Dlatego MyConsumer jest bardziej szczegółowy niż Consumer ponieważ Consumer (T w specyfikacji) nie jest podtypem i oba mają wartość powrotną void.