2015-10-08 37 views
18

Myślę, że to pytanie jest już gdzieś tam, ale nie byłem w stanie go znaleźć.Dlaczego potrzebuję funkcjonalnego interfejsu do pracy z lambdami?

Nie rozumiem, dlaczego konieczne jest posiadanie funkcjonalnego interfejsu do pracy z lambdami. Rozważmy następujący przykład:

public class Test { 

    public static void main(String...args) { 
     TestInterface i =() -> System.out.println("Hans"); 
//  i = (String a) -> System.out.println(a); 

     i.hans(); 
//  i.hans("Hello"); 
    } 
} 

public interface TestInterface { 
    public void hans(); 
// public void hans(String a); 
} 

To działa bezproblemowo, ale jeśli odkomentujesz skomentowane linie, to nie. Czemu? W moim rozumieniu, kompilator powinien być w stanie odróżnić obie metody, ponieważ mają różne parametry wejściowe. Dlaczego potrzebuję funkcjonalnego interfejsu i wysadzić mój kod?

EDYCJA: Połączone duplikaty nie odpowiedziały na moje pytanie, ponieważ pytam o różne parametry metody. Ale dostałem tutaj kilka naprawdę użytecznych odpowiedzi, dzięki wszystkim, którzy pomogli! :)

EDIT2: Niestety, jestem oczywiście nie native speaker, ale precyzyjny siebie:

public interface TestInterface { 
    public void hans();     //has no input parameters</br> 
    public void hans(String a);   //has 1 input parameter, type String</br> 
    public void hans(String a, int b); //has 2 input parameters, 1. type = String, 2. type = int</br> 
    public void hans(int a, int b);  //has also 2 input parameters, but not the same and a different order than `hans(String a, int a);`, so you could distinguish both 
} 

public class Test { 

    public static void main(String...args) { 
     TestInterface i =() -> System.out.println("Hans"); 
     i = (String a) -> System.out.println(a); 
     i = (String a, int b) -> System.out.println(a + b); 
     i = (int a, int b) -> System.out.println(a); 

     i.hans(2, 3); //Which method would be called? Of course the one that would take 2 integer arguments. :) 
    } 
} 

Wszystko pytam jest o argumentach. Nazwa metody nie ma znaczenia, ale każda z metod przyjmuje unikalną kolejność różnych argumentów iz tego powodu Oracle mogło zaimplementować tę funkcję, zamiast tego po prostu umożliwić pojedynczą metodę na "Lambda-Interface".

+3

'int i = 7; i = 5; System.out.println (i); // nie byłoby wspaniale, gdyby wydrukowało to zarówno 7, jak i 5? ' – immibis

+1

Naprawdę nie rozumiem twojego rozumowania. Jeśli coś nie implementuje interfejsu, to jak może być tego typu? IPod nie liczy się jako iPhone, tylko dlatego, że nie można wykonywać połączeń telefonicznych. – zeroflagL

+0

@immibis: To, co tutaj opisujesz, różni się od tego, o co prosiłem. :) – codepleb

Odpowiedz

20

Kiedy piszesz:

TestInterface i =() -> System.out.println("Hans"); 

Dajesz implementację do sposobu TestInterfacevoid hans().

Jeśli można przypisać wyrażenie lambda do interfejsu posiadającego więcej niż jedną metodę abstrakcyjną (tj. Interfejs niefunkcjonalny), wyrażenie lambda może zaimplementować tylko jedną z metod, pozostawiając inne metody niezatwierdzonymi.

Nie można tego rozwiązać, przypisując dwie wyrazy lambda o różnych sygnaturach do tej samej zmiennej (Tak samo, jak nie można przypisać referencji dwóch obiektów do pojedynczej zmiennej i oczekiwać, że zmienna odnosi się do obu obiektów jednocześnie) .

+0

Hm, brzmi logicznie, ale wciąż zastanawiam się, dlaczego Oracle tak to zrobiło. Gdybyś mógł po prostu wywołać metody, dla których zaimplementowałeś ciało, byłby bardziej intuicyjny. Jest to oczywiście problem koncepcji interfejsu, a nie samej lambdas. Dziękuję Ci. :) – codepleb

+2

@TrudleR, a następnie Java byłaby językiem funkcjonalnym. Jeśli jesteś zainteresowany taką implementacją na JVM. Możesz wypróbować język Scala. –

+0

Hm, Scala Scala Scala. : D Myślę, że naprawdę muszę uczyć się tego języka. Wszyscy mówią o tym nareszcie. Czy mój przypadek jest możliwy w tym języku? To byłoby naprawdę piękne. – codepleb

3

Nie trzeba tworzyć funkcjonalnego interfejsu w celu utworzenia funkcji lambda. Interfejs umożliwia utworzenie instancji do przyszłego wywoływania funkcji.

W twoim przypadku można wykorzystać już istniejące interfejsu Runable

Runnable r =() -> System.out.println("Hans");

a następnie zadzwonić

r.run();

Można myśleć o lambda -> jak tylko krótki ręka dla:

Runnable r = new Runnable() { 
    void run() { 
      System.out.println("Hans");` 
    } 
} 

Z lambdą nie potrzebujesz anonimowej klasy, która jest tworzona pod maską w powyższym przykładzie.

Ale to ma pewne ograniczenia, aby dowiedzieć się, jaka metoda powinna być nazywana interfejs używany z lambdas musi być SAM (Single Abstract Method). Wtedy mamy tylko jedną metodę.

Bardziej szczegółowe wyjaśnienie przeczytać:

Introduction to Functional Interfaces – A Concept Recreated in Java 8

13

Najważniejszym powodem, dla którego muszą one zawierać tylko jedną metodę, jest to, że zamieszanie jest łatwo możliwe inaczej. Jeśli w interfejsie dopuszczono wiele metod, to która metoda powinna wybrać lambdę, jeśli listy argumentów są takie same?

interface TestInterface { 
    void first(); 
    void second(); // this is only distinguished from first() by method name 
    String third(); // maybe you could say in this instance "well the return type is different" 
    Object fourth(); // but a String is an Object, too ! 
} 

void test() { 
    // which method are you implementing, first or second ? 
    TestInterface a =() -> System.out.println("Ido mein ado mein"); 
    // which method are you implementing, third or fourth ? 
    TestInterface b =() -> return "Ido mein ado mein"; 
} 
+0

Tak, wiem, że powinien po prostu spowodować wyjątek, jeśli chcesz uruchomić taki kod. Ale myślałem, że różne argumenty powinny być jasne dla kompilatora. Jeśli zadeklarujesz dwie metody o tej samej nazwie i parametrach, to również daje wyjątek, więc dlaczego Oracle ma temu zapobiec? – codepleb

+0

Dobry przykład. Dzięki – greenhorn

3

Wydaje się szuka klas anonimowych. Poniższy kod działa:

public class Test { 

    public static void main(String...args) { 
     TestInterface i = new TestInterface() { 
      public void hans() { 
       System.out.println("Hans"); 
      } 
      public void hans(String a) { 
       System.out.println(a); 
      } 
     }; 

     i.hans(); 
     i.hans("Hello"); 
    } 
} 

public interface TestInterface { 
    public void hans(); 
    public void hans(String a); 
} 

wyrażenia lambda są (w większości) krótsza droga do pisania anonimowych klas z tylko jednej metody. (Podobnie, anonimowe klasy są skrótem dla klas wewnętrznych, których używasz tylko w jednym miejscu)

+0

Hej, nie jestem zbyt początkującym. :) Oczywiście wiem, w jaki sposób mogę go uruchomić. Chciałem tylko zapytać, dlaczego lambdy są przycinane do pracy tylko z funkcjonalnymi interfejsami. Jest oczywiste, że będę musiał napisać anonimowe klasy (lub zaimplementować samą klasę) dla mojej sprawy. Chciałem tylko powiedzieć, że sądzę, że Oracle mógł rozszerzyć wykorzystanie lambdas. Ale po dyskusji tutaj jest jasne, dlaczego zmuszają cię do korzystania z funkcjonalnych interfejsów. Ale i tak dziękuję. – codepleb

+0

@TrudleR Jeśli rozszerzają lambdas w sposób, w jaki myślisz, to będą takie same jak anonimowe klasy, więc o co w tym chodzi? – immibis

+0

precyzyjna odpowiedź dzięki. – greenhorn