JavaRush /Java blogi /Random-UZ /Lambda ifodalari misollar bilan

Lambda ifodalari misollar bilan

Guruhda nashr etilgan
Java dastlab butunlay ob'ektga yo'naltirilgan tildir. Ibtidoiy turlardan tashqari, Java-da hamma narsa ob'ektdir. Hatto massivlar ham ob'ektlardir. Har bir sinfning misollari ob'ektlardir. Funktsiyani alohida belgilashning yagona imkoniyati yo'q (sinfdan tashqari - taxminan transl. ). Va usulni argument sifatida o'tkazish yoki boshqa usul natijasida usul tanasini qaytarishning hech qanday usuli yo'q. Xuddi shunday. Ammo bu Java 8 dan oldin edi. Lambda ifodalari misollar bilan - 1Qadimgi Swing davridan boshlab, ba'zi bir funksiyalarni qandaydir metodga o'tkazish zarur bo'lganda, anonim sinflarni yozish kerak edi. Misol uchun, hodisa ishlov beruvchisini qo'shish shunday ko'rinishga ega edi:
someObject.addMouseListener(new MouseAdapter() {
            public void mouseClicked(MouseEvent e) {

                //Event listener implementation goes here...

            }
        });
Bu erda biz sichqoncha hodisasi tinglovchisiga ba'zi kod qo'shmoqchimiz. Biz anonim sinfni aniqladik MouseAdapterva darhol undan ob'ekt yaratdik. Shu tarzda, biz qo'shimcha funksiyalarni addMouseListener. Xulosa qilib aytganda, Java-da oddiy usulni (funksionallikni) argumentlar orqali o'tkazish oson emas. Ushbu cheklov Java 8 dasturchilarini til spetsifikatsiyasiga Lambda ifodalari kabi xususiyatni qo'shishga majbur qildi.

Nima uchun Java Lambda ifodalariga muhtoj?

Java tili boshidanoq Annotatsiyalar, Generics va hokazolardan tashqari unchalik rivojlanmagan. Birinchidan, Java har doim ob'ektga yo'naltirilgan bo'lib kelgan. JavaScript kabi funktsional tillar bilan ishlagandan so'ng, Java qanday qilib qat'iy ob'ektga yo'naltirilganligini va kuchli terilganligini tushunish mumkin. Java-da funksiyalar kerak emas. O'z-o'zidan ularni Java dunyosida topib bo'lmaydi. Funktsional dasturlash tillarida funksiyalar birinchi o'ringa chiqadi. Ular o'z-o'zidan mavjud. Siz ularni o'zgaruvchilarga belgilashingiz va ularni argumentlar orqali boshqa funktsiyalarga o'tkazishingiz mumkin. JavaScript funktsional dasturlash tillarining eng yaxshi namunalaridan biridir. Internetda JavaScript-ning funktsional til sifatida afzalliklari haqida batafsil ma'lumot beradigan yaxshi maqolalarni topishingiz mumkin. Funktsional tillar ilovalarni yozishning an'anaviy usullariga nisbatan bir qator afzalliklarni ta'minlovchi Closure kabi kuchli vositalarga ega. Yopish - bu unga biriktirilgan muhitga ega funksiya - funktsiyaning barcha mahalliy bo'lmagan o'zgaruvchilariga havolalarni saqlaydigan jadval. Java'da yopilishlarni Lambda ifodalari orqali simulyatsiya qilish mumkin. Albatta, yopilishlar va Lambda iboralari o'rtasida kichik emas, balki farqlar mavjud, ammo lambda iboralari yopilishlarga yaxshi muqobildir. Stiv Yegge o'zining istehzoli va kulgili blogida Java dunyosi otlar (ob'ektlar, ob'ektlar - taxminan. tarjima ) bilan qanday qilib qat'iy bog'langanligini tasvirlaydi. Agar siz uning blogini o'qimagan bo'lsangiz, uni tavsiya qilaman. U Lambda iboralari Java-ga nima uchun qo'shilganligining aniq sababini kulgili va qiziqarli tarzda tasvirlab beradi. Lambda iboralari Java-ga uzoq vaqt davomida etishmayotgan funksionallikni olib keladi. Lambda iboralari xuddi ob'ektlar kabi tilga funksionallik keltiradi. Bu 100% to'g'ri bo'lmasa-da, Lambda iboralari yopiq bo'lmasa ham, shunga o'xshash imkoniyatlarni taqdim etishini ko'rishingiz mumkin. Funktsional tilda lambda ifodalari funksiyalardir; ammo Java-da lambda ifodalari ob'ektlar bilan ifodalanadi va ular funktsional interfeys deb ataladigan muayyan ob'ekt turi bilan bog'lanishi kerak. Keyinchalik bu nima ekanligini ko'rib chiqamiz. Mario Fuskoning "Bizga Java-da Lambda ifodasi nima uchun kerak" maqolasida nima uchun barcha zamonaviy tillar yopish qobiliyatiga muhtojligi batafsil tavsiflangan.

Lambda ifodalariga kirish

Lambda iboralari anonim funktsiyalardir (Java uchun 100% to'g'ri ta'rif bo'lmasligi mumkin, ammo bu aniqlik keltiradi). Oddiy qilib aytganda, bu deklaratsiyasiz usul, ya'ni. kirish modifikatorlarisiz, qiymat va nomni qaytaradi. Muxtasar qilib aytganda, ular sizga usul yozish va uni darhol ishlatish imkonini beradi. Bu, ayniqsa, bir martalik usul chaqiruvida foydalidir, chunki sinf yaratmasdan usulni e'lon qilish va yozish vaqtini qisqartiradi. Java'dagi lambda ifodalari odatda quyidagi sintaksisga ega (аргументы) -> (тело). Masalan:
(арг1, арг2...) -> { тело }

(тип1 арг1, тип2 арг2...) -> { тело }
Quyida haqiqiy Lambda ifodalarining ba'zi misollari keltirilgan:
(int a, int b) -> {  return a + b; }

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

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

() -> 42

() -> { return 3.1415 };

Lambda ifodalarining tuzilishi

Lambda ifodalarining tuzilishini o‘rganamiz:
  • Lambda ifodalari 0 yoki undan ortiq kirish parametrlariga ega bo'lishi mumkin.
  • Parametrlar turi aniq ko'rsatilishi yoki kontekstdan olinishi mumkin. Masalan ( int a) shunday yozilishi mumkin ( a)
  • Parametrlar qavslar ichiga olinadi va vergul bilan ajratiladi. Masalan ( a, b) yoki ( int a, int b) yoki ( String a, int b, float c)
  • Agar parametrlar bo'lmasa, siz bo'sh qavslardan foydalanishingiz kerak. Masalan() -> 42
  • Faqat bitta parametr mavjud bo'lganda, turi aniq ko'rsatilmagan bo'lsa, qavslar olib tashlanishi mumkin. Misol:a -> return a*a
  • Lambda ifodasining tanasi 0 yoki undan ortiq ifodani o'z ichiga olishi mumkin.
  • Agar tana bitta bayonotdan iborat bo'lsa, u jingalak qavslar ichiga kiritilmasligi mumkin va qaytariladigan qiymat kalit so'zsiz ko'rsatilishi mumkin return.
  • Aks holda, jingalak qavslar kerak bo'ladi (kod bloki) va qaytarish qiymati kalit so'z yordamida oxirida ko'rsatilishi kerak return(aks holda qaytarish turi bo'ladi void).

Funktsional interfeys nima

Java-da Marker interfeyslari usullar yoki maydonlarni e'lon qilmaydigan interfeyslardir. Boshqacha qilib aytganda, token interfeyslari bo'sh interfeyslardir. Xuddi shunday, Funktsional interfeyslar faqat bitta mavhum usul e'lon qilingan interfeyslardir. java.lang.Runnablefunksional interfeysga misol bo‘la oladi. U faqat bitta usulni e'lon qiladi void run(). Bundan tashqari, interfeys mavjud ActionListener- funktsional. Ilgari biz funktsional interfeysni amalga oshiradigan ob'ektlarni yaratish uchun anonim sinflardan foydalanishimiz kerak edi. Lambda ifodalari bilan hamma narsa soddalashdi. Har bir lambda ifodasi ba'zi funktsional interfeysga bevosita bog'lanishi mumkin. Masalan, Runnablequyidagi misolda ko'rsatilganidek, interfeysga havola yaratishingiz mumkin:
Runnable r = () -> System.out.println("hello world");
Funktsional interfeys ko'rsatilmaganda, bu turdagi konvertatsiya har doim bevosita amalga oshiriladi:
new Thread(
    () -> System.out.println("hello world")
).start();
Yuqoridagi misolda kompilyator Runnablesinf konstruktoridan interfeysni amalga oshirish sifatida avtomatik ravishda lambda ifodasini yaratadi Thread: public Thread(Runnable r) { }. Bu erda lambda ifodalari va mos keladigan funktsional interfeyslarning ba'zi misollari:
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 };
Java 8-da Java tili spetsifikatsiyasiga muvofiq qo'shilgan izoh @FunctionalInterfacee'lon qilingan interfeysning ishlashini tekshiradi. Bundan tashqari, Java 8 Lambda ifodalari bilan foydalanish uchun bir qator tayyor funktsional interfeyslarni o'z ichiga oladi. @FunctionalInterfaceAgar e'lon qilingan interfeys ishlamasa, kompilyatsiya xatosini keltirib chiqaradi. Quyida funktsional interfeysni aniqlashga misol keltirilgan:
@FunctionalInterface
public interface WorkerInterface {

    public void doSomeWork();

}
Ta'rifdan ko'rinib turibdiki, funktsional interfeys faqat bitta mavhum usulga ega bo'lishi mumkin. Agar siz boshqa mavhum usul qo'shmoqchi bo'lsangiz, siz kompilyatsiya xatosini olasiz. Misol:
@FunctionalInterface
public interface WorkerInterface {

    public void doSomeWork();

    public void doSomeMoreWork();

}
Xato
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") );
    }

}
Xulosa:
Worker вызван через анонимный класс
Worker вызван через Lambda
Bu erda biz o'z funktsional interfeysimizni aniqladik va lambda ifodasidan foydalandik. Usul execute()lambda ifodalarini argument sifatida qabul qilishga qodir.

Lambda ifodalariga misollar

Lambda iboralarini tushunishning eng yaxshi usuli bir nechta misollarni ko'rib chiqishdir: Oqim Threadikki usulda ishga tushirilishi mumkin:
// 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();
Java 8 da hodisalarni boshqarish Lambda ifodalari orqali ham amalga oshirilishi mumkin. Quyidagilar ActionListenerUI komponentiga hodisa ishlov beruvchisini qo‘shishning ikki yo‘li:
// 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!");
});
Berilgan massivning barcha elementlarini ko'rsatishning oddiy misoli. E'tibor bering, lambda ifodasini ishlatishning bir nechta usullari mavjud. Quyida biz strelkalar sintaksisi yordamida odatiy tarzda lambda ifodasini yaratamiz, shuningdek (::), Java 8 da oddiy usulni lambda ifodasiga aylantiradigan ikki nuqta operatoridan foydalanamiz:
// 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);
Quyidagi misolda biz Predicatetest yaratish va ushbu testdan o'tgan narsalarni chop etish uchun funktsional interfeysdan foydalanamiz. Shunday qilib, siz lambda iboralariga mantiqni qo'yishingiz va unga asoslangan narsalarni qilishingiz mumkin.
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();
    }

}
Xulosa:
Выводит все числа: 1 2 3 4 5 6 7
Не выводит ни одного числа:
Вывод четных чисел: 2 4 6
Вывод нечетных чисел: 1 3 5 7
Вывод чисел больше 5: 6 7
Lambda iboralari bilan ishlash orqali siz ro'yxatning har bir elementining kvadratini ko'rsatishingiz mumkin. stream()E'tibor bering, biz oddiy ro'yxatni oqimga aylantirish usulidan foydalanamiz . Java 8 ajoyib sinfni taqdim etadi Stream( java.util.stream.Stream). Unda lambda ifodalaridan foydalanishingiz mumkin bo'lgan tonnalab foydali usullar mavjud. Biz lambda ifodasini oqimdagi barcha elementlarga qo'llaydigan x -> x*xusulga o'tkazamiz. map()Shundan so'ng biz forEachro'yxatning barcha elementlarini chop etish uchun foydalanamiz.
// 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);
Ro'yxat berilgan bo'lsa, siz ro'yxatning barcha elementlarining kvadratlari yig'indisini chop etishingiz kerak. Lambda ifodalari bunga faqat bitta qator kod yozish orqali erishish imkonini beradi. Ushbu misolda konvolyutsiya (kamaytirish) usuli qo'llaniladi reduce(). Biz har bir elementni kvadratga solish usulidan foydalanamiz map()va keyin reduce()barcha elementlarni bitta raqamga yig'ish usulidan foydalanamiz.
// 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);

Lambda ifodalari va anonim sinflar o'rtasidagi farq

Asosiy farq - kalit so'zdan foydalanish this. Anonim sinflar uchun ' ' kalit so'zi thisanonim sinf ob'ektini bildiradi, lambda ifodasida esa ' this' lambda ifodasi ishlatiladigan sinf ob'ektini bildiradi. Yana bir farq - ularni tuzish usuli. privateJava lambda ifodalarini kompilyatsiya qiladi va ularni sinf usullariga aylantiradi . Bu dinamik usulni ulash uchun Java 7 da kiritilgan invokedynamic ko'rsatmasidan foydalanadi. Tal Vays o'z blogida Java qanday qilib lambda ifodalarini bayt-kodga kompilyatsiya qilishini tasvirlab berdi

Xulosa

Mark Reynxold (Oracle bosh arxitektori) Lambda ifodalarini dasturlash modelidagi eng muhim o'zgarish deb atadi - bu hatto generiklardan ham muhimroq. U haq bo'lishi kerak, chunki ... ular Java dasturchilariga hamma kutgan funktsional dasturlash tili imkoniyatlarini beradi. Virtual kengaytma usullari kabi innovatsiyalar bilan bir qatorda, Lambda ifodalari juda yuqori sifatli kod yozish imkonini beradi. Umid qilamanki, ushbu maqola sizga Java 8 qopqog'i ostida ko'rinish berdi. Omad tilaymiz :)
Izohlar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION