Dawno nie było pisania technicznego :(, troszkę jest rzeczy na głowie. Dzisiaj na tapetę idzie Java 8 i jej jeden z ficzerów operator Lambda.

Co to są Lambdy?

Jest to uproszczony sposób zapis metod anonimowych, tylko taki bardziej czytelniejszy. Lambda nawiązuje do języków funkcyjnych, gdzie tak jak w matematyce kładzie się nacisk na obliczanie wartości danego wyrażenia. W przeciwieństwie do typowych języków programowanie, gdzie ważne jest wykonywanie poleceń. Przykładem języków typowo funkcyjnych jest: Erlang, Scala.

Zastosowanie:

Lambdy mają w Javie 8 zastosowanie do:

  • operowania na kolekcjach danych
  • operowania na strumieniach danych, w połączeniu ze strumieniami oraz nowym API Nio2

Składnia polecenia lambda:

(lista parametrów) -> {polecenia;}

Gdzie:

  • lista parametrów jest opcjonalna
  • opcjonalne jest podawanie typów parametrów
  • opcjonalne są nawiasy bloku
  • nie jest wymagane wyrażenie return
  • lambdy mają dostęp do pól, metod instancji obiektu
  • lambdy mają dostęp do statycznych metod i pól klasy
  • w przypadku użycia lambdy na finalnym polu gdzie lambda ma przypisać do niego wartość dostaniemy błąd kompilacji

Przykłady:

Proste użycie gdzie nie ma określonego typu zwracanych danych, są określane na podstawie interfejsu funkcyjnego, dokonuje się prostych operacji:

package pl.jclab.examples.lambda;
 
public class Lambda1{
 
   @FunctionalInterface
   interface DoubleLambda{
      double getValue();
   }
 
   @FunctionalInterface
   interface StringLambda{
      String getValue();
   }
 
   @FunctionalInterface
   interface DoubleParameterLambda{
      double getValue(double x, double y);
   }
 
   @FunctionalInterface
   interface StringParameterLambda{
      String getValue(String x, String y);
   }
 
   @FunctionalInterface
   interface GenericLambda<T>{
      T getValue(T x, T y);
   }
 
   public static void main(String[] args){
 
      //
      // Proste przypisanie
      //
      DoubleLambda testPi = () -> 3.14;
      System.out.println(testPi.getValue());
 
      StringLambda testString = () -> "SuperDuper";
      System.out.println(testString.getValue());
 
      //
      // Operacje na danych z kilkoma parametrami zdefiniowanymi w interface
      //
      DoubleParameterLambda testMultipler = (x, y) -> x * y;
      System.out.println(testMultipler.getValue(3, 4));
 
      DoubleParameterLambda testAdd = (x, y) -> x + y;
      System.out.println(testAdd.getValue(3, 4));
 
      DoubleParameterLambda testDiv = (x, y) -> x / y;
      System.out.println(testAdd.getValue(3, 4));
 
      //
      // Trochę zabawy na tekstach, pokazany dostęp do metod
      //
      StringParameterLambda toUpperAndLower = (x, y) -> x.toUpperCase() + " " + y.toLowerCase();
      System.out.println(toUpperAndLower.getValue("DEMO", "DEMO"));
      System.out.println(toUpperAndLower.getValue("demo", "demo"));
 
 
      //
      // Przykład z generykiem
      //
      GenericLambda<Double> testGeneric = (x, y) -> x * y;
      System.out.println(testGeneric.getValue(5.0, 10.0));
 
      GenericLambda<Integer> testGeneric2 = (x, y) -> x * y;
      System.out.println(testGeneric2.getValue(5, 10));
 
      GenericLambda<String> testGeneric3 = (x, y) -> x + "_" + y;
      System.out.println(testGeneric3.getValue("5", "10"));
   }
}

Przykład bardziej złożonej lambdy, gdzie ciało lambdy wykonuje obliczenia, są pętle oraz warunki sterowania, wywalić wyjątkiem w twarz:

package pl.jclab.examples.lambda;
 
public class Lambda2{
 
   @FunctionalInterface
   interface OneParameter{
      double calculate(int x) throws IllegalArgumentException;
   }
 
 
   public static void main(String[] args){
 
      //
      // W ciele lambdy realizowane są złożone operacje
      //
 
      //
      // Silnia
      //
      OneParameter factorial = (x) -> {
         int summary = 1;
         for (int i = 1; i <= x; ++i){
            summary = i * summary;
         }
         return summary;
      };
 
      System.out.println(factorial.calculate(1));
      System.out.println(factorial.calculate(2));
      System.out.println(factorial.calculate(5));
      System.out.println(factorial.calculate(10));
 
 
      //
      // Ciąg fibonaccigo
      //
      OneParameter fibonacci = (x) -> {
 
         if (x == 0){
            throw new IllegalArgumentException();
         }
 
         if (x == 1 || x == 2){
            return 1;
         }
 
         int a = 0, b = 1, fibb = 1;
         for (int i = 2; i <= x; i++){
            fibb = a + b;
            a = b;
            b = fibb;
         }
         return fibb;
      };
 
      System.out.println(fibonacci.calculate(1));
      System.out.println(fibonacci.calculate(2));
      System.out.println(fibonacci.calculate(5));
      System.out.println(fibonacci.calculate(10));
   }
}

Wielowątkowe wywołania:

package pl.jclab.examples.lambda;
 
public class Lambda3{
   public static void main(String[] args){
 
      Runnable run = () -> System.out.println("args = [" + args + "]");
      Runnable run2 = () -> {
         int a = 10;
         int b = 10;
         System.out.println("a+b=" + a + b);
      };
 
      run.run();
      run2.run();
   }
}

Użycie w kolekcjach i tablicach, porównywanie – lambda jako komparator w przypadku sortowania danych:

package pl.jclab.examples.lambda;
 
import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
 
public class Lambda4{
 
   String[] names = {
   "Marcia", "Darrel", "Sandee", "Trena", "Clark", "Almeta", "Bettyann", "Yael", "Harvey", "Andy"
   };
 
   public static void main(String[] args){
 
 
      Lambda4 lambda4 = new Lambda4();
 
      //
      // Sorotowanie przy użyciu funkcji anonimowych
      //
      List<String> namesList = Arrays.asList(lambda4.names);
      Collections.sort(namesList, new Comparator<String>(){
         @Override
         public int compare(String o1, String o2){
            return o1.compareTo(o2);
         }
      });
 
      System.out.println("Sort ASC collection by anonymous inner class: " + namesList.toString());
 
      //
      // Sortowanie przy użyciu funkcji lambda
      //
      namesList = Arrays.asList(lambda4.names);
      Collections.sort(namesList, (s1, s2) -> s1.compareTo(s2));
      System.out.println("Sort ASC collection by lambda: " + namesList.toString());
 
      //
      // Sortowanie przy użyciu funkcji lambda i statycznej metody
      //
      namesList = Arrays.asList(lambda4.names);
      Collections.sort(namesList, String::compareTo);
      System.out.println("Sort ASC collection by static method: " + namesList.toString());
 
      //
      // Sortowanie odwrotne przy użyciu funkcji lambda
      //
      namesList = Arrays.asList(lambda4.names);
      Collections.sort(namesList, (s1, s2) -> -s1.compareTo(s2));
      System.out.println("Sort DESC collection by lambda: " + namesList.toString());
   }
}

Użycie w kolekcjach – forach, filtrowanie danych:

package pl.jclab.examples.lambda;
 
import java.util.Arrays;
import java.util.List;
 
public class Lambda5{
 
   private static String[] names = {
   "Marcia ", "Darrel ", "Sandee ", "Trena ", "Clark ", "Almeta ", "Bettyann ", "Yael ", "Harvey ", "Andy "
   };
 
   public static void main(String[] args){
 
      //
      // Standartowy for
      //
      List<String> namesList = Arrays.asList(Lambda5.names);
 
      System.out.println("Standard for:");
      for (String name : namesList){
         System.out.print(name);
      }
      System.out.println();
 
      //
      // ForEach przy pomocy lambdy
      //
      System.out.println("Lambda for:");
      namesList.forEach(s -> System.out.print(s));
      System.out.println();
 
      System.out.println("Lambda for static method:");
      namesList.forEach(System.out::print);
      System.out.println();
 
 
      //
      // Modyfikacja i filtrowanie danych przy pomocy strumieni i lambdy
      //
      System.out.println("Stream filter and map:");
      namesList.stream()
             .filter(s -> s.startsWith("A"))
             .map(s -> s + " ")
             .forEach(System.out::print);
      System.out.println();
 
      //
      // Sortowanie i modyfikacja danych przy pomocy strumieni i lambdy
      //
      System.out.println("Stream sort and map:");
      namesList.stream()
             .sorted()
             .map(s -> s + " ")
             .map(String::toUpperCase)
             .forEach(System.out::print);
      System.out.println();
   }
}