JavaRush /Java Blog /Random-TL /Mga expression ng Lambda na may mga halimbawa

Mga expression ng Lambda na may mga halimbawa

Nai-publish sa grupo
Ang Java sa simula ay isang ganap na object-oriented na wika. Maliban sa mga primitive na uri, ang lahat sa Java ay isang bagay. Kahit na ang mga array ay mga bagay. Ang mga pagkakataon ng bawat klase ay mga bagay. Walang isang solong posibilidad na tukuyin ang isang function nang hiwalay (sa labas ng isang klase - tinatayang transl. ). At walang paraan upang maipasa ang isang pamamaraan bilang argumento o ibalik ang katawan ng pamamaraan bilang resulta ng isa pang pamamaraan. Parang ganun. Ngunit ito ay bago ang Java 8. Mga expression ng Lambda na may mga halimbawa - 1Dahil ang mga araw ng magandang lumang Swing, ito ay kinakailangan upang magsulat ng mga hindi kilalang klase kapag ito ay kinakailangan upang ipasa ang ilang functionality sa ilang paraan. Halimbawa, ganito ang hitsura ng pagdaragdag ng isang event handler:
someObject.addMouseListener(new MouseAdapter() {
            public void mouseClicked(MouseEvent e) {

                //Event listener implementation goes here...

            }
        });
Dito gusto naming magdagdag ng ilang code sa tagapakinig ng kaganapan ng mouse. Tinukoy namin ang isang hindi kilalang klase MouseAdapterat agad na lumikha ng isang bagay mula dito. Sa ganitong paraan, naipasa namin ang karagdagang paggana sa addMouseListener. Sa madaling salita, hindi madaling ipasa ang isang simpleng pamamaraan (functionality) sa Java sa pamamagitan ng mga argumento. Pinilit ng limitasyong ito ang mga developer ng Java 8 na magdagdag ng tampok tulad ng mga expression ng Lambda sa detalye ng wika.

Bakit kailangan ng Java ang mga expression ng Lambda?

Sa simula pa lang, ang wikang Java ay hindi gaanong nagbago, maliban sa mga bagay tulad ng Anotasyon, Generics, atbp. Una sa lahat, ang Java ay palaging nananatiling object-oriented. Pagkatapos magtrabaho sa mga functional na wika tulad ng JavaScript, mauunawaan ng isa kung paano mahigpit na object-oriented ang Java at malakas ang pag-type. Hindi kailangan ang mga function sa Java. Sa kanilang sarili, hindi sila matatagpuan sa mundo ng Java. Sa functional programming language, nauuna ang mga function. Sila ay umiiral sa kanilang sarili. Maaari mong italaga ang mga ito sa mga variable at ipasa ang mga ito sa pamamagitan ng mga argumento sa iba pang mga function. Ang JavaScript ay isa sa mga pinakamahusay na halimbawa ng functional programming language. Makakahanap ka ng magagandang artikulo sa Internet na nagdedetalye ng mga benepisyo ng JavaScript bilang isang functional na wika. Ang mga functional na wika ay may makapangyarihang mga tool tulad ng Closure, na nagbibigay ng ilang mga pakinabang sa mga tradisyonal na paraan ng pagsulat ng mga application. Ang pagsasara ay isang function na may environment na naka-attach dito - isang table na nag-iimbak ng mga reference sa lahat ng hindi lokal na variable ng function. Sa Java, ang mga pagsasara ay maaaring gayahin sa pamamagitan ng mga expression ng Lambda. Siyempre, may mga pagkakaiba sa pagitan ng mga pagsasara at mga expression ng Lambda, at hindi mga maliliit, ngunit ang mga expression ng lambda ay isang magandang alternatibo sa mga pagsasara. Sa kanyang mapanukso at nakakatawang blog, inilalarawan ni Steve Yegge kung paano mahigpit na nakatali ang mundo ng Java sa mga pangngalan (mga entidad, bagay - tinatayang transl. ). Kung hindi mo pa nababasa ang kanyang blog, inirerekumenda ko ito. Inilalarawan niya sa nakakatawa at kawili-wiling paraan ang eksaktong dahilan kung bakit idinagdag ang mga expression ng Lambda sa Java. Ang mga expression ng Lambda ay nagdadala ng functionality sa Java na matagal nang nawawala. Ang mga expression ng Lambda ay nagdadala ng functionality sa wika tulad ng mga bagay. Bagama't hindi ito 100% totoo, makikita mo na ang mga expression ng Lambda, habang hindi mga pagsasara, ay nagbibigay ng mga katulad na kakayahan. Sa isang functional na wika, ang mga expression ng lambda ay mga function; ngunit sa Java, ang mga expression ng lambda ay kinakatawan ng mga bagay, at dapat na nauugnay sa isang partikular na uri ng bagay na tinatawag na isang functional na interface. Susunod ay titingnan natin kung ano ito. Ang artikulo ni Mario Fusco na "Bakit kailangan natin ng Lambda Expression sa Java" ay naglalarawan nang detalyado kung bakit ang lahat ng modernong wika ay nangangailangan ng mga kakayahan sa pagsasara.

Panimula sa Lambda Expressions

Ang mga expression ng Lambda ay mga hindi kilalang function (maaaring hindi isang 100% tamang kahulugan para sa Java, ngunit nagdudulot ito ng kaunting kalinawan). Sa madaling salita, ito ay isang paraan na walang deklarasyon, i.e. walang mga modifier ng access, nagbabalik na halaga at pangalan. Sa madaling salita, pinapayagan ka nilang magsulat ng isang paraan at gamitin ito kaagad. Ito ay lalong kapaki-pakinabang sa kaso ng isang beses na paraan ng tawag, dahil binabawasan ang oras na kinakailangan upang magdeklara at magsulat ng isang pamamaraan nang hindi kinakailangang lumikha ng isang klase. Ang mga expression ng Lambda sa Java ay karaniwang may sumusunod na syntax (аргументы) -> (тело). Halimbawa:
(арг1, арг2...) -> { тело }

(тип1 арг1, тип2 арг2...) -> { тело }
Nasa ibaba ang ilang halimbawa ng totoong Lambda expression:
(int a, int b) -> {  return a + b; }

() -> System.out.println("Hello World");

(String s) -> { System.out.println(s); }

() -> 42

() -> { return 3.1415 };

Istraktura ng Lambda Expressions

Pag-aralan natin ang istruktura ng mga expression ng lambda:
  • Ang mga expression ng Lambda ay maaaring magkaroon ng 0 o higit pang mga parameter ng pag-input.
  • Ang uri ng mga parameter ay maaaring tahasang tukuyin o maaaring makuha mula sa konteksto. Halimbawa ( int a) ay maaaring isulat ng ganito ( a)
  • Ang mga parameter ay nakapaloob sa panaklong at pinaghihiwalay ng mga kuwit. Halimbawa ( a, b) o ( int a, int b) o ( String a, int b, float c)
  • Kung walang mga parameter, kailangan mong gumamit ng mga walang laman na panaklong. Halimbawa() -> 42
  • Kapag mayroon lamang isang parameter, kung ang uri ay hindi tahasang tinukoy, ang mga panaklong ay maaaring tanggalin. Halimbawa:a -> return a*a
  • Ang katawan ng isang Lambda expression ay maaaring maglaman ng 0 o higit pang mga expression.
  • Kung ang katawan ay binubuo ng isang pahayag, maaaring hindi ito nakapaloob sa mga kulot na brace, at maaaring tukuyin ang return value nang wala ang keyword return.
  • Kung hindi, ang mga kulot na brace ay kinakailangan (block ng code) at ang return value ay dapat na tukuyin sa dulo gamit ang isang keyword return(kung hindi, ang return type ay void).

Ano ang isang functional na interface

Sa Java, ang mga interface ng Marker ay mga interface nang hindi nagdedeklara ng mga pamamaraan o field. Sa madaling salita, ang mga interface ng token ay mga walang laman na interface. Katulad nito, ang Mga Functional Interface ay mga interface na may isang abstract na paraan lamang na ipinahayag dito. java.lang.Runnableay isang halimbawa ng isang functional na interface. Nagpahayag lamang ito ng isang paraan void run(). Mayroon ding isang interface ActionListener- gumagana din. Noong nakaraan, kailangan naming gumamit ng mga hindi kilalang klase upang lumikha ng mga bagay na nagpapatupad ng isang functional na interface. Sa mga expression ng Lambda, naging mas simple ang lahat. Ang bawat lambda expression ay maaaring implicitly na nakatali sa ilang functional na interface. Halimbawa, maaari kang lumikha ng isang sanggunian sa Runnableisang interface, tulad ng ipinapakita sa sumusunod na halimbawa:
Runnable r = () -> System.out.println("hello world");
Ang ganitong uri ng conversion ay palaging ginagawa nang tahasan kapag hindi kami tumukoy ng functional na interface:
new Thread(
    () -> System.out.println("hello world")
).start();
Sa halimbawa sa itaas, awtomatikong gumagawa ang compiler ng lambda expression bilang pagpapatupad Runnableng interface mula sa class constructor Thread: public Thread(Runnable r) { }. Narito ang ilang halimbawa ng mga expression ng lambda at kaukulang functional interface:
Consumer<Integer> c = (int x) -> { System.out.println(x) };

BiConsumer<Integer, String> b = (Integer x, String y) -> System.out.println(x + " : " + y);

Predicate<String> p = (String s) -> { s == null };
Ang annotation @FunctionalInterfacena idinagdag sa Java 8 ayon sa Java Language Specification ay nagsusuri kung gumagana ang ipinahayag na interface. Bilang karagdagan, ang Java 8 ay may kasamang bilang ng mga nakahanda nang functional na interface para magamit sa mga expression ng Lambda. @FunctionalInterfaceay magtapon ng error sa compilation kung hindi gumagana ang ipinahayag na interface. Ang sumusunod ay isang halimbawa ng pagtukoy ng isang functional na interface:
@FunctionalInterface
public interface WorkerInterface {

    public void doSomeWork();

}
Tulad ng iminumungkahi ng kahulugan, ang isang functional na interface ay maaaring magkaroon lamang ng isang abstract na pamamaraan. Kung susubukan mong magdagdag ng isa pang abstract na paraan, makakakuha ka ng error sa compilation. Halimbawa:
@FunctionalInterface
public interface WorkerInterface {

    public void doSomeWork();

    public void doSomeMoreWork();

}
Error
Unexpected @FunctionalInterface annotation
    @FunctionalInterface ^ WorkerInterface is not a functional interface multiple
    non-overriding abstract methods found in interface WorkerInterface 1 error
После определения функционального интерфейса, мы можем его использовать и получать все преимущества Lambda-выражений. Пример:// defining a functional interface
@FunctionalInterface
public interface WorkerInterface {

    public void doSomeWork();

}
public class WorkerInterfaceTest {

    public static void execute(WorkerInterface worker) {
        worker.doSomeWork();
    }

    public static void main(String [] args) {

      // calling the doSomeWork method via an anonymous class
      // (classic)
      execute(new WorkerInterface() {
            @Override
            public void doSomeWork() {
               System.out.println("Worker called via an anonymous class");
            }
        });

      // calling the doSomeWork method via Lambda expressions
      // (Java 8 new)
      execute( () -> System.out.println("Worker called via Lambda") );
    }

}
Konklusyon:
Worker вызван через анонимный класс
Worker вызван через Lambda
Dito namin tinukoy ang aming sariling functional interface at gumamit ng lambda expression. Ang pamamaraan execute()ay may kakayahang tanggapin ang mga expression ng lambda bilang isang argumento.

Mga halimbawa ng Lambda expression

Ang pinakamahusay na paraan upang maunawaan ang mga expression ng Lambda ay ang tumingin sa ilang mga halimbawa: ThreadMaaaring masimulan ang isang stream sa dalawang paraan:
// Old way:
new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("Hello from thread");
    }
}).start();
// New way:
new Thread(
    () -> System.out.println("Hello from thread")
).start();
Ang pamamahala ng kaganapan sa Java 8 ay maaari ding gawin sa pamamagitan ng mga expression ng Lambda. Ang sumusunod ay dalawang paraan upang magdagdag ng tagapangasiwa ng kaganapan ActionListenersa isang bahagi ng UI:
// Old way:
button.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        System.out.println("Button pressed. Old way!");
    }
});
// New way:
button.addActionListener( (e) -> {
        System.out.println("Button pressed. Lambda!");
});
Isang simpleng halimbawa ng pagpapakita ng lahat ng elemento ng isang naibigay na array. Tandaan na mayroong higit sa isang paraan upang gumamit ng lambda expression. Sa ibaba ay gumagawa kami ng lambda expression sa karaniwang paraan gamit ang arrow syntax, at ginagamit din namin ang double colon operator (::), na sa Java 8 ay nagko-convert ng isang regular na paraan sa isang lambda expression:
// Old way:
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
for(Integer n: list) {
    System.out.println(n);
}
// New way:
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
list.forEach(n -> System.out.println(n));
// New way using double colon operator ::
list.forEach(System.out::println);
Sa sumusunod na halimbawa, gumagamit kami ng functional na interface Predicatepara gumawa ng pagsubok at mag-print ng mga item na pumasa sa pagsubok na iyon. Sa ganitong paraan maaari kang maglagay ng lohika sa mga expression ng lambda at gumawa ng mga bagay batay dito.
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;

public class Main {

    public static void main(String [] a)  {

        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);

        System.out.print("Outputs all numbers: ");
        evaluate(list, (n)->true);

        System.out.print("Does not output any number: ");
        evaluate(list, (n)->false);

        System.out.print("Output even numbers: ");
        evaluate(list, (n)-> n%2 == 0 );

        System.out.print("Output odd numbers: ");
        evaluate(list, (n)-> n%2 == 1 );

        System.out.print("Output numbers greater than 5: ");
        evaluate(list, (n)-> n > 5 );

    }

    public static void evaluate(List<Integer> list, Predicate<Integer> predicate) {
        for(Integer n: list)  {
            if(predicate.test(n)) {
                System.out.print(n + " ");
            }
        }
        System.out.println();
    }

}
Konklusyon:
Выводит все числа: 1 2 3 4 5 6 7
Не выводит ни одного числа:
Вывод четных чисел: 2 4 6
Вывод нечетных чисел: 1 3 5 7
Вывод чисел больше 5: 6 7
Sa pamamagitan ng pag-iisip sa mga expression ng Lambda, maaari mong ipakita ang parisukat ng bawat elemento ng listahan. Pansinin na ginagamit namin ang paraan stream()upang i-convert ang isang regular na listahan sa isang stream. Ang Java 8 ay nagbibigay ng isang kahanga-hangang klase Stream( java.util.stream.Stream). Naglalaman ito ng napakaraming kapaki-pakinabang na pamamaraan na magagamit mo sa mga expression ng lambda. Nagpapasa kami ng lambda expression x -> x*xsa method map(), na nalalapat ito sa lahat ng elemento sa stream. Pagkatapos nito ay ginagamit namin forEachupang i-print ang lahat ng mga elemento ng listahan.
// Old way:
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7);
for(Integer n : list) {
    int x = n * n;
    System.out.println(x);
}
// New way:
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7);
list.stream().map((x) -> x*x).forEach(System.out::println);
Dahil sa isang listahan, kailangan mong i-print ang kabuuan ng mga parisukat ng lahat ng mga elemento ng listahan. Nagbibigay-daan sa iyo ang mga expression ng Lambda na makamit ito sa pamamagitan ng pagsusulat lamang ng isang linya ng code. Ang halimbawang ito ay gumagamit ng convolution (reduction) method reduce(). Gumagamit kami ng isang paraan map()upang i-square ang bawat elemento, at pagkatapos ay gumamit ng isang paraan reduce()upang i-collapse ang lahat ng mga elemento sa isang solong numero.
// Old way:
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7);
int sum = 0;
for(Integer n : list) {
    int x = n * n;
    sum = sum + x;
}
System.out.println(sum);
// New way:
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7);
int sum = list.stream().map(x -> x*x).reduce((x,y) -> x + y).get();
System.out.println(sum);

Ang pagkakaiba sa pagitan ng mga expression ng Lambda at mga hindi kilalang klase

Ang pangunahing pagkakaiba ay ang paggamit ng keyword this. Para sa mga anonymous na klase, ang keyword na ' ' thisay tumutukoy sa isang object ng anonymous na klase, habang sa isang lambda expression, ' this' ay tumutukoy sa isang object ng klase kung saan ginagamit ang lambda expression. Ang isa pang pagkakaiba ay ang paraan ng pagsasama-sama ng mga ito. Kino-compile ng Java ang mga expression ng lambda at kino-convert ang mga ito sa privatemga pamamaraan ng klase. Ito ay gumagamit ng invokedynamic na pagtuturo , na ipinakilala sa Java 7 para sa dynamic na paraan na nagbubuklod. Inilarawan ni Tal Weiss sa kanyang blog kung paano kino-compile ng Java ang mga expression ng lambda sa bytecode

Konklusyon

Tinawag ni Mark Reinhold (Punong Arkitekto ng Oracle) ang mga expression ng Lambda na pinakamahalagang pagbabago sa modelo ng programming na naganap - kahit na mas makabuluhan kaysa sa mga generic. Tiyak na tama siya, dahil... binibigyan nila ang mga Java programmer ng functional programming language na mga kakayahan na hinihintay ng lahat. Kasama ng mga inobasyon gaya ng mga pamamaraan ng Virtual extension, binibigyang-daan ka ng mga expression ng Lambda na magsulat ng napakataas na kalidad na code. Sana ay binigyan ka ng artikulong ito ng pagtingin sa ilalim ng hood ng Java 8. Good luck :)
Mga komento
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION