یہ مضمون کس کے لیے ہے؟
مواد کی کوئی علمی قدر ہونے کا دعویٰ نہیں ہے، نیاپن کو چھوڑ دیں۔ بالکل اس کے برعکس: میں ان چیزوں کو بیان کرنے کی کوشش کروں گا جو پیچیدہ ہیں (کچھ لوگوں کے لیے) جتنا ممکن ہو۔ اسٹریم 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
منتقل کرنے کی کوشش کرے گی ، جو اس کے بجائے کسی چیز کی توقع کر رہا ہے۔ void
forEach()
Consumer
طریقہ کے حوالہ جات کے لیے نحو
یہ کافی آسان ہے:-
ہم اس طرح ایک جامد طریقہ کا حوالہ دیتے ہیں:
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 } }
-
ہم موجودہ آبجیکٹ کا استعمال کرتے ہوئے ایک غیر جامد طریقہ کا حوالہ دیتے ہیں، جیسے:
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 } }
-
ہم کلاس کا استعمال کرتے ہوئے ایک غیر جامد طریقہ کا حوالہ دیتے ہیں جو اسے مندرجہ ذیل طور پر لاگو کرتا ہے:
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); } } }
-
ہم اس طرح کے کنسٹرکٹر کا حوالہ دیتے ہیں:
ClassName::new
طریقہ کے حوالہ جات بہت آسان ہوتے ہیں جب آپ کے پاس پہلے سے کوئی طریقہ موجود ہو جو کال بیک کے طور پر بالکل کام کرے گا۔ اس صورت میں، طریقہ کے کوڈ پر مشتمل لیمبڈا ایکسپریشن لکھنے کے بجائے، یا لیمبڈا ایکسپریشن لکھنے کے جو محض طریقہ کو کہتے ہیں، ہم صرف اس کا حوالہ دیتے ہیں۔ اور یہ بات ہے.
گمنام کلاسز اور لیمبڈا اظہار کے درمیان ایک دلچسپ فرق
ایک گمنام کلاس میں،this
کلیدی لفظ گمنام کلاس کی کسی چیز کی طرف اشارہ کرتا ہے۔ لیکن اگر ہم اسے لیمبڈا کے اندر استعمال کرتے ہیں، تو ہم کنٹیننگ کلاس کے آبجیکٹ تک رسائی حاصل کر لیتے ہیں۔ ایک جہاں ہم نے اصل میں لیمبڈا اظہار لکھا ہے۔ ایسا اس لیے ہوتا ہے کیونکہ لیمبڈا ایکسپریشنز کو کلاس کے نجی طریقہ میں مرتب کیا جاتا ہے جس میں وہ لکھے جاتے ہیں۔ میں اس "فیچر" کو استعمال کرنے کی سفارش نہیں کروں گا، کیونکہ اس کا ایک ضمنی اثر ہے اور یہ فنکشنل پروگرامنگ کے اصولوں سے متصادم ہے۔ اس نے کہا، یہ نقطہ نظر مکمل طور پر OOP کے ساتھ مطابقت رکھتا ہے۔ ;)
مجھے اپنی معلومات کہاں سے ملی اور آپ کو اور کیا پڑھنا چاہیے؟
- اوریکل کی آفیشل ویب سائٹ پر ٹیوٹوریل۔ مثالوں سمیت بہت ساری تفصیلی معلومات۔
- اسی اوریکل ٹیوٹوریل میں طریقہ کے حوالہ جات کے بارے میں باب ۔
- اگر آپ واقعی متجسس ہیں تو ویکیپیڈیا میں شامل ہوں ۔
GO TO FULL VERSION