Wyrażenia lambda #1

W wersji Java 8 pojawiła się charakterystyczna i tajemnicza strzałka „->” Nie oznacza ona bynajmniej kierunku pisania kodu w Javie 🙂 Strzałka ta jest symbolem wyrażeń lambda.

<!– end –>

W prostych słowach wyrażenia lambda to funkcje anonimowe. Nie określałbym ich metodami, bo lambda wcale nie przynależy do konkretnej klasy jak metody w języku Java. Wyrażenie lambdy nie posiada także nazwy, nie muszą być zdefiniowane typy parametrów, nie muszą być nawet przekazane parametry, posiada ciało i może zwracać konkretny typ lub nie. Ciekawostką jest to, że lambda może być przesłana do metody jako parametr lub przypisana do zmiennej. Jak widać lambda pozwala na duża ekspresję 🙂

W skrócie wyrażenie lambda to:

  • funkcja bez nazwy np:
(int a, int b) -> a + b;
  • nie musi posiadać typów parametrów:
(a, b) -> a + b;
  • nie musi posiadać parametrów:
() -> 1 + 1;
  • nie musi posiadać czegokolwiek 😉
() -> {};
  • może coś zwracać:
() -> {return 1 + 1;};
  • może coś zwracać nawet bez return:
() -> 1+1;
  • może nic nie zwracać:
() -> System.out.println("Lambda");
  • można ją przypisać do zmiennej:
Runnable runnable = () -> {};
  • zmienną można przekazać do metody:
runTask(runnable);

Zapewne te przykłady jeszcze niczego konkretnego nie mówią, ale postaram się wyjaśnić to w dalszej części. Na początku omówmy jeszcze jak wygląda powołanie do życia lambdy:

InterfaceCalculation add = (a, b) -> a + b;

Po lewej stronie mamy nazwę interfejsu funkcjonalnego, który wygląda tak:

@FunctionalInterface
interface InterfaceCalculation {

   int calculation(int a, int b);
}

Jest on niezbędny, żeby mogła powstać nasza funkcja anonimowa (lambda). O interfejsach funkcyjnych będę jeszcze pisał. Tak więc do zmiennej *add* przypisałem wyrażenie lambda. Parametry *(a,b)* to parametry jakie będą przesłane do tego wyrażenia. Mogą one wyglądać również tak *(int a, int b)* jak w tradycyjnej metodzie, ale nie muszą 😉 Zauważcie, że lambda ma dokładnie tyle samo parametrów co zdefiniowana metoda w interfejsie.

Po strzałce, która tak czy siak wskazuje kierunek pisania, mamy ciało naszej funkcji, w której możemy wykonać pewne działania. Tutaj po prostu dodaję dwie przesłane liczby. Jeżeli będziemy chcieli umieścić więcej linii kodu, całe ciało musimy otoczyć nawiasami klamrowymi np:

 InterfaceCalculation add = (a, b) -> {
     if (a < 0) {
      a=3;
    }
     return a + b;
  };

Czyli mamy interfejs, a w nim zdefiniowaną metodę. Jak to w interfejsie. Za pomocą lambdy możemy przypisać do zmiennej add, która jest typem tego interfejsu, implementację jego metody wyrażeniem lambda. W tym przypadku jest to implementacja metody calculation(int a, int b). Możemy teraz wywołać metodę *calculation* z naszego interfejsu, która przekaże parametry do naszej lambdy i zrealizuje ją. Po prostu wywołujemy metodę calculation na obiekcie add, który jest typem naszego interfejsu.

 //realizuję wyrażenie lambda które "siedzi" w add, lambda zwraca wynik do zmiennej result
int result = add.calculation(-1, 3);
//wypisuję wynik
System.out.println("Wynik to: " + result);

Wynik to: 6

Oczywiście, metodę tego interfejsu można zaimplementować przez klasę anonimową:

// Klasa anonimowa -stary brzydki sposób
InterfaceCalculation interfaceCalculation = new InterfaceCalculation() {
     @Override public int calculation(int a, int b) {
       return a + b;
    }
  };

// To samo ale lambda
InterfaceCalculation add = (a, b) -> a + b;

Jednak sami widzicie jak wiele zbędnego kodu trzeba stworzyć. Jest on mało czytelny i słabo testowalny. Nie polecam 🙂

Mam nadzieje, że wstęp do lambd wydaje się jasny, jeżeli coś nie jest jasne, piszcie w komentarzach. Na bieżąco będę starał się wyjaśniać. W następnym wpisie opiszę interfejs funkcjonalny bo jest on nierozłącznie związany z wyrażeniami lambda. A po tym zrobimy konkretny praktyczny odcinek z przykładami.