JavaRush /جاوا بلاگ /Random-SD /جاوا ۾ lambda اظهار بابت مشهور. مثالن ۽ ڪمن سان. حصو 2

جاوا ۾ lambda اظهار بابت مشهور. مثالن ۽ ڪمن سان. حصو 2

گروپ ۾ شايع ٿيل
هي مضمون ڪنهن لاءِ آهي؟
  • انهن لاءِ جيڪي هن مضمون جو پهريون حصو پڙهندا آهن؛

  • انهن لاءِ جيڪي سمجهن ٿا ته اهي اڳ ۾ ئي جاوا ڪور کي چڱي طرح ڄاڻن ٿا، پر جاوا ۾ لامبڊا اظهار بابت ڪا ڄاڻ ناهي. يا، شايد، توهان اڳ ۾ ئي ڪجهه ٻڌو آهي ليمبڊاس بابت، پر تفصيل کان سواء.

  • انهن لاءِ جن کي لامبڊا جي اظهار جي ڪجهه سمجهه آهي، پر اڃا تائين انهن کي استعمال ڪرڻ کان ڊپ ۽ غير معمولي آهن.

خارجي متغيرات تائين رسائي

ڇا هي ڪوڊ هڪ گمنام طبقي سان گڏ ڪندو؟
int counter = 0;
Runnable r = new Runnable() {
    @Override
    public void run() {
        counter++;
    }
};
نه. متغير counterهجڻ گهرجي final. يا ضروري ناهي final، پر ڪنهن به صورت ۾ اهو ان جي قيمت کي تبديل نٿو ڪري سگهي. ساڳيو اصول lambda اظهار ۾ استعمال ڪيو ويندو آهي. انهن کي انهن سڀني متغيرن تائين رسائي آهي جيڪي انهن کي "ظاهر" آهن جتان اهي اعلان ڪيا ويا آهن. پر لامبڊا انهن کي تبديل نه ڪرڻ گهرجي (نئين قيمت مقرر ڪريو). سچ، گمنام طبقن ۾ هن حد کي نظرانداز ڪرڻ جو اختيار آهي. اهو ڪافي آهي صرف هڪ حوالن جي قسم جو هڪ متغير ٺاهڻ ۽ اعتراض جي اندروني حالت کي تبديل ڪرڻ لاء. انهي صورت ۾، متغير پاڻ کي ساڳئي اعتراض ڏانهن اشارو ڪندو، ۽ انهي صورت ۾ توهان محفوظ طور تي ان کي ظاهر ڪري سگهو ٿا final.
final AtomicInteger counter = new AtomicInteger(0);
Runnable r = new Runnable() {
    @Override
    public void run() {
        counter.incrementAndGet();
    }
};
هتي اسان جو متغير counterهڪ قسم جي اعتراض جو حوالو آهي AtomicInteger. ۽ ھن اعتراض جي حالت کي تبديل ڪرڻ لاء، طريقو استعمال ڪيو ويندو آھي incrementAndGet(). متغير جي قيمت پاڻ تبديل نه ٿيندي آهي جڏهن ته پروگرام هلندي آهي ۽ هميشه هڪ ئي اعتراض ڏانهن اشارو ڪري ٿو، جيڪو اسان کي هڪ متغير کي فوري طور تي لفظ سان اعلان ڪرڻ جي اجازت ڏئي ٿو final. ساڳيا مثال، پر لامبدا اظهار سان:
int counter = 0;
Runnable r = () -> counter++;
اهو هڪ ئي سبب سان گڏ نه آهي جيئن هڪ گمنام طبقي سان اختيار: counterان کي تبديل نه ٿيڻ گهرجي جڏهن پروگرام هلندي آهي. پر هن وانگر - سڀ ڪجهه ٺيڪ آهي:
final AtomicInteger counter = new AtomicInteger(0);
Runnable r = () -> counter.incrementAndGet();
اهو پڻ سڏڻ جي طريقن تي لاڳو ٿئي ٿو. ليمبڊا جي اظهار جي اندر کان، توهان نه صرف رسائي حاصل ڪري سگهو ٿا سڀ ”ڏسندڙ“ متغيرات، پر انهن طريقن کي پڻ سڏين ٿا جيڪي توهان وٽ آهن.
public class Main {
    public static void main(String[] args) {
        Runnable runnable = () -> staticMethod();
        new Thread(runnable).start();
    }

    private static void staticMethod() {
        System.out.println("Я - метод staticMethod(), и меня только-что кто-то вызвал!");
    }
}
جيتوڻيڪ اهو طريقو staticMethod()خانگي آهي، اهو "رسائي لائق" آهي جنهن کي طريقي جي اندر سڏيو وڃي ٿو main()، تنهنڪري اهو پڻ دستياب آهي اندر کان ڪال ڪرڻ لاء ليمبڊا جيڪو طريقو ۾ ٺاهيو ويو آهي main.

lambda ايڪسپريس ڪوڊ جي عملدرآمد جو لمحو

اهو سوال توهان کي تمام سادو لڳي سگهي ٿو، پر اهو سڀ ڪجهه پڇڻ جي لائق آهي: لامبڊا ايڪسپريس جي اندر ڪوڊ کي ڪڏهن جاري ڪيو ويندو؟ تخليق جي وقت تي؟ يا ان وقت جڏهن (اڃا تائين نامعلوم آهي) اهو ڪٿي سڏيو ويندو؟ اهو چيڪ ڪرڻ بلڪل آسان آهي.
System.out.println("Запуск программы");

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

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

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

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

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

System.out.println("Перед передачей лямбды в тред");
new Thread(runnable).start();
ڊسپلي تي آئوٽ:
Запуск программы
Перед объявлением лямбды
После объявления лямбды
Перед передачей лямбды в тред
Я - лямбда!
اهو ڏسي سگھجي ٿو ته لامبڊا ايڪسپريس ڪوڊ تمام آخر ۾ عمل ڪيو ويو، موضوع جي ٺهڻ کان پوء ۽ صرف جڏهن پروگرام جي عمل جي عمل کي طريقي جي حقيقي عمل تي پهچي ويو run(). ۽ نه ئي ان جي اعلان جي وقت تي. هڪ لامبڊا اظهار جو اعلان ڪندي، اسان صرف قسم جو هڪ اعتراض ٺاهيو Runnable۽ ان جي طريقي جي رويي کي بيان ڪيو run(). طريقو پاڻ کي گهڻو پوء شروع ڪيو ويو.

طريقو حوالو؟

سڌو سنئون لامبڊاس سان لاڳاپيل ناهي، پر منهنجو خيال آهي ته هن مضمون ۾ ان بابت ڪجهه لفظ چوڻ منطقي هوندو. اچو ته اسان وٽ هڪ لامبڊا اظهار آهي جيڪو ڪجهه خاص نٿو ڪري، پر صرف ڪجهه طريقو سڏيندو آهي.
x -> System.out.println(x)
هنن هن کي ڪجهه ڏنو х، ۽ اهو صرف هن کي سڏيو System.out.println()۽ هن کي اتي پهچايو х. انهي حالت ۾، اسان ان کي تبديل ڪري سگھون ٿا هڪ لنڪ سان جيڪو اسان کي گهربل آهي. هن وانگر:
System.out::println
ها، بغير قوس جي آخر ۾! وڌيڪ مڪمل مثال:
List<String> strings = new LinkedList<>();
strings.add("Mother");
strings.add("soap");
strings.add("frame");

strings.forEach(x -> System.out.println(x));
آخري لڪير تي اسان هڪ طريقو استعمال ڪندا آهيون forEach()جيڪو قبول ڪري ٿو هڪ انٽرفيس اعتراض Consumer. هي وري هڪ فنڪشنل انٽرفيس آهي صرف هڪ طريقي سان void accept(T t). ان مطابق، اسان هڪ ليمبڊا ايڪسپريشن لکون ٿا جيڪو هڪ پيرا ميٽر وٺي ٿو (جيئن ته اهو انٽرفيس ۾ ئي ٽائيپ ڪيو ويو آهي، اسان پيراميٽر جي قسم کي ظاهر نه ڪندا آهيون، پر اهو اشارو ڪيو ويندو ته ان کي سڏيو ويندو х). lambda ايڪسپريس جي جسم ۾ اسان ڪوڊ لکندا آهيون. جنهن تي عمل ڪيو ويندو جڏهن ميٿڊ کي سڏيو ويندو accept(). هتي اسان صرف اسڪرين تي ڏيکاريون ٿا ته متغير ۾ ڇا آهي х. طريقو پاڻ کي forEach()گڏ ڪرڻ جي سڀني عنصرن مان گذري ٿو، Consumerان کي پاس ڪيل انٽرفيس اعتراض جي طريقي کي سڏي ٿو (اسان جي lambda) accept()، جتي اهو هر عنصر کي ڪليڪشن مان پاس ڪري ٿو. جيئن مون اڳ ۾ چيو آهي، هي هڪ lambda-expression آهي (صرف هڪ ٻئي طريقي کي سڏيندي) اسان کي گهربل طريقي جي حوالي سان تبديل ڪري سگهون ٿا. پوء اسان جو ڪوڊ هن طرح نظر ايندو:
List<String> strings = new LinkedList<>();
strings.add("Mother");
strings.add("soap");
strings.add("frame");

strings.forEach(System.out::println);
بنيادي شيء اها آهي ته طريقن جي قبول ٿيل پيراگراف (println()۽ accept()). جيئن ته طريقو println()ڪنهن به شيءِ کي قبول ڪري سگهي ٿو (اهو سڀني ابتدائي ۽ ڪنهن به شئي لاءِ اوور لوڊ ٿيل آهي)، ليمبڊا ايڪسپريشن جي بدران، اسان forEach()صرف طريقي جي حوالي سان پاس ڪري سگهون ٿا println(). پوء forEach()اهو مجموعي جي هر عنصر کي وٺي ۽ سڌو ان کي منتقل ڪندو. println()انهن لاءِ جيڪي پهريون ڀيرو هن سان منهن ڏئي رهيا آهن، مهرباني ڪري نوٽ ڪريو ته اسان ان طريقي کي نه ٿا سڏيون (لفظن جي وچ ۾ System.out.println()نقطا ۽ آخر ۾ بریکٹ سان)، بلڪه اسان هن طريقي جو حوالو پاڻ ڏانهن منتقل ڪريون ٿا.
strings.forEach(System.out.println());
اسان وٽ گڏ ڪرڻ جي غلطي هوندي. ڇاڪاڻ ته ڪال کان اڳ forEach()، جاوا ڏسندو ته ان کي سڏيو پيو وڃي System.out.println()، اهو سمجھندو ته اهو واپس ڪيو پيو وڃي ۽ ان کي ان قسم جي اعتراض ڏانهن منتقل ڪرڻ جي voidڪوشش ڪندو جيڪو اتي انتظار ڪري رهيو آهي . voidforEach()Consumer

طريقن جي حوالي سان استعمال ڪرڻ لاء نحو

اهو تمام سادو آهي:
  1. هڪ جامد طريقي جي حوالي سان پاس ڪرڻNameКласса:: 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. موجوده اعتراض کي استعمال ڪندي غير جامد طريقي جي حوالي سان پاس ڪرڻNameПеременнойСОбъектом:: 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. اسان ڪلاس استعمال ڪندي هڪ غير جامد طريقي جو حوالو ڏيو ٿا جنهن ۾ اهڙي طريقي تي عمل ڪيو ويو آهيNameКласса:: 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. ڪنسٽرڪٽر ڏانهن لنڪ پاس NameКласса::new
    ڪرڻ جو طريقو لنڪس استعمال ڪرڻ تمام آسان آهي جڏهن هڪ تيار ٿيل طريقو آهي جنهن سان توهان مڪمل طور تي مطمئن آهيو، ۽ توهان ان کي ڪال بڪ طور استعمال ڪرڻ چاهيندا. انهي صورت ۾، انهي طريقي جي ڪوڊ سان ليمبڊا ايڪسپريس لکڻ جي بدران، يا هڪ ليمبڊا ايڪسپريس جتي اسان صرف هن طريقي کي سڏين ٿا، اسان صرف ان جو حوالو ڏيو. اهو ئي سڀ ڪجهه آهي.

گمنام طبقي ۽ لامبڊا اظهار جي وچ ۾ دلچسپ فرق

هڪ گمنام طبقي ۾، لفظ thisان گمنام طبقي جي اعتراض ڏانهن اشارو ڪري ٿو. ۽ جيڪڏهن اسان ان کي استعمال ڪريون ٿا thisليمبڊا اندر، اسان حاصل ڪنداسين فريمنگ ڪلاس جي اعتراض تائين. جتي اسان اصل ۾ هي اظهار لکيو آهي. اهو ٿي سگهي ٿو ڇاڪاڻ ته لامبڊا اظهار، جڏهن مرتب ڪيو وڃي ٿو، طبقي جو هڪ خانگي طريقو بڻجي وڃي ٿو جتي اهي لکيل آهن. مان هن ”فيچر“ کي استعمال ڪرڻ جي سفارش نه ڪندس، ڇاڪاڻ ته ان جو هڪ طرفي اثر آهي، جيڪو فنڪشنل پروگرامنگ جي اصولن جي تضاد آهي. پر اهو طريقو OOP سان ڪافي مطابقت رکي ٿو. ؛)

مون کي معلومات ڪٿان ملي يا ٻيو ڇا پڙهان

تبصرا
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION