wyrażenie lambda - jak działa?

0

cześć, uczę się wyrażeń lamba i próbuje zrozumieć jedną rzecz - napisałem z pomocą tutoriala prosty program, który sortuje przykładowe łańcuchy znaków wg. ich długości:

Scanner source = new Scanner(System.in);
		String[] mass = new String[4];
		
		System.out.println("Podaj 4 łańcuchy znaków");
		mass[0] = source.nextLine();
		mass[1] = source.nextLine();
		mass[2] = source.nextLine();
		mass[3] = source.nextLine();

		System.out.println(Arrays.toString(mass));
		Arrays.sort(mass, (first, second) -> first.length()-second.length());
		System.out.println(Arrays.toString(mass));

Pytanie dotyczy samego wyrażenia - jak to działa i skąd program wie co ma od czego odjąć? w wyrażeniu nie podaje nigdzie po indeksach co od czego ma odjąć, a i tak wyświetla posortowaną tablicę. Czy ktoś byłby chętny żeby mi to rozjaśnić? :)

ps. na pozostałą część kodu nie zwracamy uwagi, czysty bałagan operacyjny

3

Kontrakt metody Arrays.sort() mowi tak:

  1. daj mi w pierwszym parametrze tablice

  2. daj mi w drugim parameterze jakąś informację, jak mam posortowac ci tą tablice.

  3. Jest zrealizowane w taki sposob, ze argument ten ma byc obiektem implementujacym interfejs Comparator.
    Interfejs Comparator ma jedna metode

int compare(T o1, T o2);

Kontrakt tej metody mowi jasno:

 /**
     * Compares its two arguments for order.  Returns a negative integer,
     * zero, or a positive integer as the first argument is less than, equal
     * to, or greater than the second.<p>
.......

Jako, ze Java pozwala na definiowanie klas anonimowych, twoj przyklad mozna zapisac tak. (w wersji sprzed javy 8)

 Arrays.sort(mass, new Comparator<String>() {
            @Override
            public int compare(String first, String second) {
                return first.length()-second.length();
            }
        });

A to po prostu mowi, ze ma byc tablica Stringow ma byc posortowana wedlug ich dlugosci.

Java 8 wprowadzila lambdy i interfejsy funkcyjne. W szczegolnosci funkcyjne interfejsy (czyli takie, ktore maja jedna abstrakcyjna metoda wymagana do zaimplementowania) mozna przeksztalcic w lambde. Lambda to nic innego jak lukier syntaktyczny na obiekt klasy anonimowej implementujacy interfejs, ktory zastepuje.
Tak wiec zwiezlejszym zapisem tego

new Comparator<String>() {
            @Override
            public int compare(String first, String second) {
                return first.length()-second.length();
            }
        }

jest to

(first, second) -> first.length()-second.length()

Porownaj sobie oba snippet'y i zobacz, ktore elementy zostaly usuniete, a ktore odpowiadaja tym z dluzszej wersji powyzej.
Nie musisz podawac typow dla zmiennych first i second w danej lambdzie, dlatego ze do jezyka weszlo tez inferencja typow (type inference) i kompilator jest w stanie stwierdzic, ze skoro tablica jest tablica Stringow, to first i second ma byc typu string.

Sama implementacja funkcji sort jest malo istotna. Ta funkcja przejdzie ci po calej tablicy i w odpowiedni sposob ci ja posortuje, ty musisz tylko powiedziec jej, wedlug jakiego kryterium. Tym kryterium tutaj jest dlugosc Stringow a forma w jaka ta funkcje informujesz, jest poprzez powiedzenie jej, ktory element jest mniejszy/wiekszy/czy sa rowne jesli natrafi na dwa obiekty typu String. Funkcja sort jest przykladem funcji wyzszego rzedu (higher order function).

0

@Leroy: dzięki za wyjaśnienie, teraz już rozumiem, mam tylko jedno. w projekcie mojego programu nie ma żadnego interfejsu. co by zmieniło gdybym go dodał?

1

Małe sprostowanie - lambdą można zaimplementować nie tylko interfejs, ale i klasę abstrakcyjną. Warunek jest ten sam - ma być dokładnie jedna metoda abstrakcyjna (tzn bez ciała). Typy z jedną metodą abstrakcyjną określa się skrótem SAM (single abstract method) i ten skrót często przewija się obok opisu lambd z Javy 8+.

0

@pawku69: nie rozumiem pytania. Wspomniany interfejs jest zdefiniowany w SDK Javy i ta metoda wymaga jego implementacji jako argument.

0

@Leroy: w moim kodzie nie ma "...implements Comparator". No chyba ze dzieki temu, ze interfejs jest zdefiniowany w SDK nie jest to konieczne?

0

Jak przekazujesz lambdę to automatycznie tworzy się z tej lambdy obiekt anonimowej nienazwanej klasy która ma to "... implements Comparable" zrobione ale nie widać tego w kodzie źródłowym.

1 użytkowników online, w tym zalogowanych: 0, gości: 1