JavaRush /Java Blog /Random-TL /Sikat tungkol sa mga expression ng lambda sa Java. Sa mga...

Sikat tungkol sa mga expression ng lambda sa Java. Sa mga halimbawa at gawain. Bahagi 2

Nai-publish sa grupo
Para kanino ang artikulong ito?
  • Para sa mga nagbabasa ng unang bahagi ng artikulong ito;

  • Para sa mga nag-iisip na alam na nila ang Java Core, ngunit walang ideya tungkol sa mga expression ng lambda sa Java. O, marahil, may narinig ka na tungkol sa mga lambda, ngunit walang mga detalye.

  • para sa mga may kaunting pag-unawa sa mga expression ng lambda, ngunit natatakot at hindi pangkaraniwang gamitin ang mga ito.

Pag-access sa mga panlabas na variable

Mag-compile ba ang code na ito sa isang hindi kilalang klase?
int counter = 0;
Runnable r = new Runnable() {
    @Override
    public void run() {
        counter++;
    }
};
Hindi. Ang variable counteray dapat na final. O hindi naman final, ngunit sa anumang kaso hindi nito mababago ang halaga nito. Ang parehong prinsipyo ay ginagamit sa mga expression ng lambda. Mayroon silang access sa lahat ng mga variable na "nakikita" sa kanila mula sa lugar kung saan sila idineklara. Ngunit hindi dapat baguhin ng lambda ang mga ito (magtalaga ng bagong halaga). Totoo, may opsyon na laktawan ang limitasyong ito sa mga hindi kilalang klase. Ito ay sapat lamang upang lumikha ng isang variable ng isang uri ng sanggunian at baguhin ang panloob na estado ng bagay. Sa kasong ito, ang variable mismo ay ituturo sa parehong bagay, at sa kasong ito maaari mong ligtas na ipahiwatig ito bilang final.
final AtomicInteger counter = new AtomicInteger(0);
Runnable r = new Runnable() {
    @Override
    public void run() {
        counter.incrementAndGet();
    }
};
Narito ang aming variable counteray isang reference sa isang object ng uri AtomicInteger. At upang baguhin ang estado ng bagay na ito, ginagamit ang pamamaraan incrementAndGet(). Ang halaga ng variable mismo ay hindi nagbabago habang tumatakbo ang program at palaging tumuturo sa parehong bagay, na nagbibigay-daan sa amin na magdeklara kaagad ng variable gamit ang keyword final. Ang parehong mga halimbawa, ngunit may mga expression ng lambda:
int counter = 0;
Runnable r = () -> counter++;
Hindi ito nag-compile para sa parehong dahilan tulad ng opsyon na may hindi kilalang klase: counterhindi ito dapat magbago habang tumatakbo ang programa. Ngunit tulad nito - maayos ang lahat:
final AtomicInteger counter = new AtomicInteger(0);
Runnable r = () -> counter.incrementAndGet();
Nalalapat din ito sa mga paraan ng pagtawag. Mula sa loob ng isang lambda expression, hindi mo lamang maa-access ang lahat ng "nakikita" na mga variable, ngunit matatawag din ang mga pamamaraan na mayroon kang access.
public class Main {
    public static void main(String[] args) {
        Runnable runnable = () -> staticMethod();
        new Thread(runnable).start();
    }

    private static void staticMethod() {
        System.out.println("Я - метод staticMethod(), и меня только-что кто-то вызвал!");
    }
}
Bagama't pribado ang pamamaraan staticMethod(), ito ay "naa-access" na tawagan sa loob ng pamamaraan main(), kaya naa-access din itong tumawag mula sa loob ng lambda na nilikha sa pamamaraan main.

Ang sandali ng pagpapatupad ng lambda expression code

Ang tanong na ito ay maaaring mukhang masyadong simple sa iyo, ngunit ito ay nagkakahalaga ng pagtatanong: kailan ang code sa loob ng lambda expression ay isasagawa? Sa sandali ng paglikha? O sa sandaling (hindi pa rin alam kung saan) tatawagin? Ito ay medyo madali upang suriin.
System.out.println("Запуск программы");

// много всякого разного codeа
// ...

System.out.println("Перед объявлением лямбды");

Runnable runnable = () -> System.out.println("Я - лямбда!");

System.out.println("После объявления лямбды");

// много всякого другого codeа
// ...

System.out.println("Перед передачей лямбды в тред");
new Thread(runnable).start();
Output na ipinapakita:
Запуск программы
Перед объявлением лямбды
После объявления лямбды
Перед передачей лямбды в тред
Я - лямбда!
Makikita na ang lambda expression code ay naisakatuparan sa pinakadulo, pagkatapos malikha ang thread at kapag ang proseso ng pagpapatupad ng programa ay umabot sa aktwal na pagpapatupad ng pamamaraan run(). At hindi sa oras ng anunsyo nito. Sa pamamagitan ng pagdedeklara ng lambda expression, gumawa lang kami ng object ng uri Runnableat inilarawan ang gawi ng pamamaraan nito run(). Ang pamamaraan mismo ay inilunsad sa ibang pagkakataon.

Mga Sanggunian sa Pamamaraan?

Hindi direktang nauugnay sa mga lambdas mismo, ngunit sa palagay ko ay lohikal na magsabi ng ilang salita tungkol dito sa artikulong ito. Sabihin nating mayroon tayong lambda expression na walang ginagawang espesyal, ngunit tumatawag lang ng ilang paraan.
x -> System.out.println(x)
May inabot sila sa kanya хat pasimpleng tinawag siya nito System.out.println()at nilagpasan siya doon х. Sa kasong ito, maaari naming palitan ito ng isang link sa paraang kailangan namin. Ganito:
System.out::println
Oo, nang walang panaklong sa dulo! Mas kumpletong halimbawa:
List<String> strings = new LinkedList<>();
strings.add("Mother");
strings.add("soap");
strings.add("frame");

strings.forEach(x -> System.out.println(x));
Sa huling linya gumagamit kami ng isang paraan forEach()na tumatanggap ng isang interface object Consumer. Ito ay muli ng isang functional na interface na may isang paraan lamang void accept(T t). Alinsunod dito, nagsusulat kami ng lambda expression na tumatagal ng isang parameter (dahil ito ay nai-type sa interface mismo, hindi namin ipinapahiwatig ang uri ng parameter, ngunit ipinapahiwatig na ito ay tatawagin х). Sa katawan ng lambda expression isinusulat namin ang code iyon ay isasagawa kapag ang pamamaraan ay tinawag na accept(). Dito ay ipinapakita lang namin sa screen kung ano ang nasa variable х. Ang pamamaraan mismo forEach()ay dumaan sa lahat ng mga elemento ng koleksyon, tinatawag Consumerang paraan ng interface na bagay na ipinasa dito (aming lambda) accept(), kung saan ipinapasa nito ang bawat elemento mula sa koleksyon. Gaya ng nasabi ko na, ito ay isang lambda -expression (pagtawag lamang ng ibang pamamaraan) maaari nating palitan ng isang reference sa pamamaraang kailangan natin. Pagkatapos ang ating code ay magiging ganito:
List<String> strings = new LinkedList<>();
strings.add("Mother");
strings.add("soap");
strings.add("frame");

strings.forEach(System.out::println);
Ang pangunahing bagay ay ang tinatanggap na mga parameter ng mga pamamaraan (println()at accept()). Dahil ang pamamaraan println()ay maaaring tumanggap ng anumang bagay (ito ay na-overload para sa lahat ng mga primitive at para sa anumang mga bagay), sa halip na isang lambda expression, maaari naming ipasa sa isang forEach()sanggunian lamang sa pamamaraan println(). Pagkatapos forEach()ay kukunin ang bawat elemento ng koleksyon at ipapasa ito nang direkta sa println()Para sa mga nakatagpo nito sa unang pagkakataon, pakitandaan Mangyaring tandaan na hindi namin tinatawag ang pamamaraan (na may mga tuldok sa pagitan System.out.println()ng mga salita at may mga bracket sa dulo), ngunit sa halip ay ipinapasa namin ang reference sa paraang ito mismo.
strings.forEach(System.out.println());
magkakaroon tayo ng compilation error. Dahil bago ang tawag forEach(), makikita ng Java na ito ay tinatawag System.out.println(), mauunawaan nito na ito ay ibinabalik voidat susubukan voidna ipasa ito sa forEach()object ng uri na naghihintay doon Consumer.

Syntax para sa paggamit ng Method References

Ito ay medyo simple:
  1. Pagpasa ng reference sa isang static na pamamaraanNameКласса:: NameСтатическогоМетода?

    public class Main {
        public static void main(String[] args) {
            List<String> strings = new LinkedList<>();
            strings.add("Mother");
            strings.add("soap");
            strings.add("frame");
    
            strings.forEach(Main::staticMethod);
        }
    
        private static void staticMethod(String s) {
            // do something
        }
    }
  2. Pagpasa ng reference sa isang non-static na pamamaraan gamit ang isang umiiral na bagayNameПеременнойСОбъектом:: method name

    public class Main {
        public static void main(String[] args) {
            List<String> strings = new LinkedList<>();
            strings.add("Mother");
            strings.add("soap");
            strings.add("frame");
    
            Main instance = new Main();
            strings.forEach(instance::nonStaticMethod);
        }
    
        private void nonStaticMethod(String s) {
            // do something
        }
    }
  3. Nagpapasa kami ng reference sa isang non-static na pamamaraan gamit ang klase kung saan ipinatupad ang naturang pamamaraanNameКласса:: method name

    public class Main {
        public static void main(String[] args) {
            List<User> users = new LinkedList<>();
            users.add(new User("Vasya"));
            users.add(new User("Коля"));
            users.add(new User("Петя"));
    
            users.forEach(User::print);
        }
    
        private static class User {
            private String name;
    
            private User(String name) {
                this.name = name;
            }
    
            private void print() {
                System.out.println(name);
            }
        }
    }
  4. Ang pagpasa ng isang link sa constructor NameКласса::new
    Ang paggamit ng mga link ng pamamaraan ay napaka-maginhawa kapag mayroong isang handa na paraan na ganap kang nasisiyahan, at nais mong gamitin ito bilang isang callback. Sa kasong ito, sa halip na magsulat ng lambda expression na may code ng paraang iyon, o isang lambda expression kung saan tinatawag lang natin ang paraang ito, nagpapasa lang tayo ng reference dito. Iyon lang.

Kawili-wiling pagkakaiba sa pagitan ng anonymous na klase at lambda expression

Sa isang hindi kilalang klase, ang keyword thisay tumuturo sa isang bagay ng hindi kilalang klaseng iyon. At kung gagamitin natin ito thissa loob ng isang lambda, makakakuha tayo ng access sa object ng framing class. Kung saan talaga namin isinulat ang expression na ito. Nangyayari ito dahil ang mga expression ng lambda, kapag pinagsama-sama, ay nagiging isang pribadong paraan ng klase kung saan nakasulat ang mga ito. Hindi ko inirerekumenda ang paggamit ng "tampok" na ito, dahil mayroon itong side effect, na sumasalungat sa mga prinsipyo ng functional programming. Ngunit ang diskarte na ito ay medyo pare-pareho sa OOP. ;)

Saan ko nakuha ang impormasyon o kung ano pa ang babasahin

Mga komento
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION