CodeGym /جاوا بلاگ /Random-UR /جاوا میں لیمبڈا اظہار کی وضاحت۔ مثالوں اور کاموں کے ساتھ۔...
John Squirrels
سطح
San Francisco

جاوا میں لیمبڈا اظہار کی وضاحت۔ مثالوں اور کاموں کے ساتھ۔ حصہ 2

گروپ میں شائع ہوا۔
یہ مضمون کس کے لیے ہے؟
  • یہ ان لوگوں کے لیے ہے جو اس مضمون کا پہلا حصہ پڑھتے ہیں۔
  • یہ ان لوگوں کے لیے ہے جو سوچتے ہیں کہ وہ جاوا کور کو پہلے ہی اچھی طرح جانتے ہیں، لیکن جاوا میں لیمبڈا اظہار کے بارے میں کوئی اشارہ نہیں ہے۔ یا ہوسکتا ہے کہ انہوں نے لیمبڈا کے تاثرات کے بارے میں کچھ سنا ہو، لیکن تفصیلات کی کمی ہے۔
  • یہ ان لوگوں کے لیے ہے جو لیمبڈا کے تاثرات کی ایک خاص سمجھ رکھتے ہیں، لیکن پھر بھی ان سے پریشان ہیں اور انہیں استعمال کرنے کے عادی نہیں ہیں۔
اگر آپ ان میں سے کسی ایک زمرے میں فٹ نہیں ہیں، تو ہو سکتا ہے کہ آپ کو یہ مضمون بورنگ، ناقص یا عام طور پر آپ کی چائے کا کپ نہ لگے۔ اس معاملے میں، بلا جھجھک دوسری چیزوں کی طرف بڑھیں یا، اگر آپ کو اس موضوع پر عبور حاصل ہے، تو براہ کرم تبصروں میں تجاویز دیں کہ میں مضمون کو کس طرح بہتر یا اضافی بنا سکتا ہوں۔ جاوا میں لیمبڈا اظہار کی وضاحت۔  مثالوں اور کاموں کے ساتھ۔  حصہ 2 - 1مواد کی کوئی علمی قدر ہونے کا دعویٰ نہیں ہے، نیاپن کو چھوڑ دیں۔ بالکل اس کے برعکس: میں ان چیزوں کو بیان کرنے کی کوشش کروں گا جو پیچیدہ ہیں (کچھ لوگوں کے لیے) جتنا ممکن ہو۔ اسٹریم API کی وضاحت کرنے کی درخواست نے مجھے یہ لکھنے کی ترغیب دی۔ میں نے اس کے بارے میں سوچا اور فیصلہ کیا کہ میری ندی کی کچھ مثالیں لیمبڈا کے تاثرات کو سمجھے بغیر ناقابل فہم ہوں گی۔ تو ہم لیمبڈا اظہار کے ساتھ شروع کریں گے۔

بیرونی متغیرات تک رسائی

کیا یہ کوڈ کسی گمنام کلاس کے ساتھ مرتب ہوتا ہے؟
int counter = 0;
Runnable r = new Runnable() {

    @Override
    public void run() {
        counter++;
    }
};
نہیں، counter متغیر ہونا چاہیے final۔ یا اگر نہیں final، تو کم از کم یہ اپنی قدر نہیں بدل سکتا۔ یہی اصول لیمبڈا اظہار میں لاگو ہوتا ہے۔ وہ ان تمام متغیرات تک رسائی حاصل کر سکتے ہیں جنہیں وہ اعلان کردہ جگہ سے "دیکھ" سکتے ہیں۔ لیکن لیمبڈا کو انہیں تبدیل نہیں کرنا چاہئے (ان کو ایک نئی قیمت تفویض کریں)۔ تاہم، گمنام کلاسوں میں اس پابندی کو نظرانداز کرنے کا ایک طریقہ موجود ہے۔ بس ایک حوالہ متغیر بنائیں اور آبجیکٹ کی اندرونی حالت کو تبدیل کریں۔ ایسا کرنے سے، متغیر خود تبدیل نہیں ہوتا ہے (ایک ہی چیز کی طرف اشارہ کرتا ہے) اور اسے محفوظ طریقے سے نشان زد کیا جا سکتا ہے final۔
final AtomicInteger counter = new AtomicInteger(0);
Runnable r = new Runnable() {

    @Override
    public void run() {
        counter.incrementAndGet();
    }
};
یہاں ہمارا متغیر کسی چیز counterکا حوالہ ہے ۔ AtomicIntegerاور incrementAndGet()طریقہ اس چیز کی حالت کو تبدیل کرنے کے لیے استعمال کیا جاتا ہے۔ پروگرام کے چلنے کے دوران متغیر کی قدر خود تبدیل نہیں ہوتی ہے۔ یہ ہمیشہ ایک ہی آبجیکٹ کی طرف اشارہ کرتا ہے، جو ہمیں حتمی مطلوبہ لفظ کے ساتھ متغیر کا اعلان کرنے دیتا ہے۔ یہاں وہی مثالیں ہیں، لیکن لیمبڈا اظہار کے ساتھ:
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("I'm staticMethod(), and someone just called me!");
    }
}
اگرچہ staticMethod()نجی ہے، یہ طریقہ کے اندر قابل رسائی ہے ، لہذا اسے طریقہ main()میں بنائے گئے لیمبڈا کے اندر سے بھی بلایا جا سکتا ہے ۔main

لیمبڈا اظہار کب عمل میں لایا جاتا ہے؟

آپ کو مندرجہ ذیل سوال بہت آسان لگ سکتا ہے، لیکن آپ کو یہ بالکل وہی پوچھنا چاہئے: lambda اظہار کے اندر کا کوڈ کب عمل میں آئے گا؟ یہ کب پیدا ہوتا ہے؟ یا جب پکارا جائے (جو ابھی تک معلوم نہیں ہے)؟ یہ چیک کرنا کافی آسان ہے۔
System.out.println("Program start");

// All sorts of code here
// ...

System.out.println("Before lambda declaration");

Runnable runnable = () -> System.out.println("I'm a lambda!");

System.out.println("After lambda declaration");

// All sorts of other code here
// ...

System.out.println("Before passing the lambda to the thread");
new Thread(runnable).start();
اسکرین آؤٹ پٹ:
Program start
Before lambda declaration
After lambda declaration
Before passing the lambda to the thread
I'm a lambda!
آپ دیکھ سکتے ہیں کہ لیمبڈا ایکسپریشن کو بالکل آخر میں عمل میں لایا گیا تھا، تھریڈ بننے کے بعد اور صرف اس وقت جب پروگرام کا عمل طریقہ تک پہنچتا ہے run()۔ یقینی طور پر نہیں جب اس کا اعلان کیا جاتا ہے۔ لیمبڈا اظہار کا اعلان کرتے ہوئے، ہم نے صرف ایک Runnableشے بنائی ہے اور بتایا ہے کہ اس کا run()طریقہ کس طرح برتاؤ کرتا ہے۔ طریقہ خود بہت بعد میں پھانسی دی جاتی ہے.

طریقہ حوالہ جات؟

طریقہ حوالہ جات براہ راست لیمبڈاس سے متعلق نہیں ہیں، لیکن میرے خیال میں اس مضمون میں ان کے بارے میں کچھ الفاظ کہنا مناسب ہے۔ فرض کریں کہ ہمارے پاس ایک لیمبڈا اظہار ہے جو کچھ خاص نہیں کرتا ہے، لیکن صرف ایک طریقہ کو کال کرتا ہے۔
x -> System.out.println(x)
یہ کچھ xاور صرف کالز وصول کرتا ہے System.out.println()، اندر گزرتا ہے x۔ اس صورت میں، ہم اسے مطلوبہ طریقہ کے حوالے سے بدل سکتے ہیں۔ اس طرح:
System.out::println
یہ ٹھیک ہے — آخر میں کوئی قوسین نہیں! یہاں ایک اور مکمل مثال ہے:
List<String> strings = new LinkedList<>();

strings.add("Dota");
strings.add("GTA5");
strings.add("Halo");

strings.forEach(x -> System.out.println(x));
آخری لائن میں، ہم forEach()طریقہ استعمال کرتے ہیں، جو ایک ایسی چیز لیتا ہے جو Consumerانٹرفیس کو لاگو کرتا ہے۔ ایک بار پھر، یہ ایک فعال انٹرفیس ہے، جس کا صرف ایک void accept(T t)طریقہ ہے۔ اس کے مطابق، ہم ایک لیمبڈا اظہار لکھتے ہیں جس کا ایک پیرامیٹر ہوتا ہے (کیونکہ یہ انٹرفیس میں ہی ٹائپ ہوتا ہے، ہم پیرامیٹر کی قسم کی وضاحت نہیں کرتے؛ ہم صرف اشارہ کرتے ہیں کہ ہم اسے کال کریں گے) x۔ لیمبڈا ایکسپریشن کے باڈی میں، ہم وہ کوڈ لکھتے ہیں جو accept()طریقہ کار کو کال کرنے پر عمل میں لایا جائے گا۔ یہاں ہم صرف ظاہر کرتے ہیں کہ متغیر میں کیا ختم ہوا x۔ یہی forEach()طریقہ مجموعہ میں موجود تمام عناصر کے ذریعے اعادہ کرتا ہے اور مجموعہ میں ہر آئٹم میں گزرتے ہوئے انٹرفیس (ہمارا لیمبڈا) accept()کے نفاذ کے طریقہ کار کو کہتے ہیں۔ Consumerجیسا کہ میں نے کہا، ہم مطلوبہ طریقہ کے حوالے سے اس طرح کے لیمبڈا اظہار کو تبدیل کر سکتے ہیں (جو محض ایک مختلف طریقہ کی درجہ بندی کرتا ہے)۔ پھر ہمارا کوڈ اس طرح نظر آئے گا:
List<String> strings = new LinkedList<>();

strings.add("Dota");
strings.add("GTA5");
strings.add("Halo");

strings.forEach(System.out::println);
println()اہم بات یہ ہے کہ اور طریقوں کے پیرامیٹرز accept()ملتے ہیں۔ کیونکہ println()طریقہ کسی بھی چیز کو قبول کر سکتا ہے (یہ تمام قدیم اقسام اور تمام اشیاء کے لیے اوورلوڈ ہوتا ہے)، لیمبڈا اظہار کے بجائے، ہم صرف طریقہ کا حوالہ دے سکتے println()ہیں forEach()۔ پھر forEach()مجموعہ میں ہر عنصر کو لے جائے گا اور اسے براہ راست println()طریقہ پر منتقل کرے گا. کسی کے لیے جو پہلی بار اس کا سامنا کر رہا ہے، براہ کرم نوٹ کریں کہ ہم کال نہیں کر رہے ہیں System.out.println()(الفاظ کے درمیان نقطوں کے ساتھ اور آخر میں قوسین کے ساتھ)۔ اس کے بجائے، ہم اس طریقہ کار کا حوالہ دے رہے ہیں۔ اگر ہم یہ لکھیں۔
strings.forEach(System.out.println());
ہمارے پاس تالیف کی غلطی ہوگی۔ کال کرنے سے پہلے forEach()، جاوا دیکھتا ہے کہ اسے System.out.println()کال کیا جا رہا ہے، لہذا یہ سمجھتا ہے کہ واپسی کی قیمت ہے اور اسے voidمنتقل کرنے کی کوشش کرے گی ، جو اس کے بجائے کسی چیز کی توقع کر رہا ہے۔ voidforEach()Consumer

طریقہ کے حوالہ جات کے لیے نحو

یہ کافی آسان ہے:
  1. ہم اس طرح ایک جامد طریقہ کا حوالہ دیتے ہیں:ClassName::staticMethodName

    public class Main {
    
        public static void main(String[] args) {
    
            List<String> strings = new LinkedList<>();
            strings.add("Dota");
            strings.add("GTA5");
            strings.add("Halo");
    
            strings.forEach(Main::staticMethod);
        }
    
        private static void staticMethod(String s) {
    
            // Do something
        }
    }
  2. ہم موجودہ آبجیکٹ کا استعمال کرتے ہوئے ایک غیر جامد طریقہ کا حوالہ دیتے ہیں، جیسے:objectName::instanceMethodName

    public class Main {
    
        public static void main(String[] args) {
    
            List<String> strings = new LinkedList<>();
            strings.add("Dota");
            strings.add("GTA5");
            strings.add("Halo");
    
            Main instance = new Main();
            strings.forEach(instance::nonStaticMethod);
        }
    
        private void nonStaticMethod(String s) {
    
            // Do something
        }
    }
  3. ہم کلاس کا استعمال کرتے ہوئے ایک غیر جامد طریقہ کا حوالہ دیتے ہیں جو اسے مندرجہ ذیل طور پر لاگو کرتا ہے:ClassName::methodName

    public class Main {
    
        public static void main(String[] args) {
    
            List<User> users = new LinkedList<>();
            users.add (new User("John"));
            users.add(new User("Paul"));
            users.add(new User("George"));
    
            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. ہم اس طرح کے کنسٹرکٹر کا حوالہ دیتے ہیں:ClassName::new

    طریقہ کے حوالہ جات بہت آسان ہوتے ہیں جب آپ کے پاس پہلے سے کوئی طریقہ موجود ہو جو کال بیک کے طور پر بالکل کام کرے گا۔ اس صورت میں، طریقہ کے کوڈ پر مشتمل لیمبڈا ایکسپریشن لکھنے کے بجائے، یا لیمبڈا ایکسپریشن لکھنے کے جو محض طریقہ کو کہتے ہیں، ہم صرف اس کا حوالہ دیتے ہیں۔ اور یہ بات ہے.

گمنام کلاسز اور لیمبڈا اظہار کے درمیان ایک دلچسپ فرق

ایک گمنام کلاس میں، thisکلیدی لفظ گمنام کلاس کی کسی چیز کی طرف اشارہ کرتا ہے۔ لیکن اگر ہم اسے لیمبڈا کے اندر استعمال کرتے ہیں، تو ہم کنٹیننگ کلاس کے آبجیکٹ تک رسائی حاصل کر لیتے ہیں۔ ایک جہاں ہم نے اصل میں لیمبڈا اظہار لکھا ہے۔ ایسا اس لیے ہوتا ہے کیونکہ لیمبڈا ایکسپریشنز کو کلاس کے نجی طریقہ میں مرتب کیا جاتا ہے جس میں وہ لکھے جاتے ہیں۔ میں اس "فیچر" کو استعمال کرنے کی سفارش نہیں کروں گا، کیونکہ اس کا ایک ضمنی اثر ہے اور یہ فنکشنل پروگرامنگ کے اصولوں سے متصادم ہے۔ اس نے کہا، یہ نقطہ نظر مکمل طور پر OOP کے ساتھ مطابقت رکھتا ہے۔ ;)

مجھے اپنی معلومات کہاں سے ملی اور آپ کو اور کیا پڑھنا چاہیے؟

اور، یقیناً، مجھے گوگل پر بہت ساری چیزیں ملیں :)
تبصرے
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION