JavaRush /جاوا بلاگ /Random-UR /مثالوں کے ساتھ لیمبڈا کے تاثرات

مثالوں کے ساتھ لیمبڈا کے تاثرات

گروپ میں شائع ہوا۔
جاوا ابتدائی طور پر ایک مکمل طور پر آبجیکٹ پر مبنی زبان ہے۔ قدیم اقسام کے استثناء کے ساتھ، جاوا میں ہر چیز ایک چیز ہے۔ یہاں تک کہ صفیں بھی آبجیکٹ ہیں۔ ہر کلاس کی مثالیں آبجیکٹ ہیں۔ کسی فنکشن کو الگ سے متعین کرنے کا ایک بھی امکان نہیں ہے (کلاس سے باہر - approx. transl. )۔ اور کسی طریقہ کو دلیل کے طور پر پاس کرنے یا کسی دوسرے طریقہ کے نتیجے میں کسی طریقہ کار کو واپس کرنے کا کوئی طریقہ نہیں ہے۔ ایسا ہی ہے۔ لیکن یہ جاوا 8 سے پہلے کی بات ہے۔ مثالوں کے ساتھ لیمبڈا کے تاثرات - 1اچھے پرانے سوئنگ کے دنوں سے، جب کچھ فنکشنلٹی کو کسی طریقہ پر منتقل کرنا ضروری ہوتا تھا تو گمنام کلاسز لکھنا ضروری تھا۔ مثال کے طور پر، ایونٹ ہینڈلر کو شامل کرنے سے ایسا لگتا ہے:
someObject.addMouseListener(new MouseAdapter() {
            public void mouseClicked(MouseEvent e) {

                //Event listener implementation goes here...

            }
        });
یہاں ہم ماؤس ایونٹ سننے والے میں کچھ کوڈ شامل کرنا چاہتے ہیں۔ ہم نے ایک گمنام کلاس کی تعریف کی MouseAdapterاور فوری طور پر اس سے ایک آبجیکٹ بنایا۔ اس طرح، ہم نے اضافی فعالیت کو میں منتقل کر دیا ہے addMouseListener۔ مختصر یہ کہ جاوا میں دلائل کے ذریعے ایک سادہ طریقہ (فعالیت) کو پاس کرنا آسان نہیں ہے۔ اس حد نے جاوا 8 کے ڈویلپرز کو زبان کی تصریح میں لیمبڈا اظہار جیسی خصوصیت شامل کرنے پر مجبور کیا۔

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

شروع سے ہی، جاوا کی زبان زیادہ ترقی نہیں کر سکی ہے، سوائے تشریحات، جنرکس وغیرہ کے۔ سب سے پہلے، جاوا ہمیشہ آبجیکٹ پر مبنی رہا ہے۔ جاوا اسکرپٹ جیسی فنکشنل زبانوں کے ساتھ کام کرنے کے بعد، کوئی سمجھ سکتا ہے کہ جاوا کس طرح سختی سے آبجیکٹ پر مبنی اور مضبوطی سے ٹائپ کیا جاتا ہے۔ جاوا میں افعال کی ضرورت نہیں ہے۔ خود سے، وہ جاوا کی دنیا میں نہیں مل سکتے ہیں۔ فنکشنل پروگرامنگ لینگویجز میں فنکشنز سامنے آتے ہیں۔ وہ اپنے طور پر موجود ہیں۔ آپ انہیں متغیرات کے لیے تفویض کر سکتے ہیں اور دلائل کے ذریعے دوسرے فنکشنز میں منتقل کر سکتے ہیں۔ جاوا اسکرپٹ فنکشنل پروگرامنگ زبانوں کی بہترین مثالوں میں سے ایک ہے۔ آپ انٹرنیٹ پر اچھے مضامین تلاش کر سکتے ہیں جن میں جاوا اسکرپٹ کے فوائد کو ایک فعال زبان کے طور پر بیان کیا گیا ہے۔ فنکشنل لینگویجز میں کلوزر جیسے طاقتور ٹولز ہوتے ہیں، جو ایپلیکیشن لکھنے کے روایتی طریقوں پر بہت سے فوائد فراہم کرتے ہیں۔ بندش ایک فنکشن ہے جس کے ساتھ ماحول منسلک ہوتا ہے - ایک ٹیبل جو فنکشن کے تمام غیر مقامی متغیرات کے حوالہ جات کو محفوظ کرتا ہے۔ جاوا میں، بندش کو لیمبڈا اظہار کے ذریعے نقل کیا جا سکتا ہے۔ بلاشبہ، بندش اور لیمبڈا اظہار کے درمیان اختلافات ہیں، اور چھوٹے نہیں، لیکن لیمبڈا اظہار بندش کا ایک اچھا متبادل ہیں۔ اپنے طنزیہ اور مضحکہ خیز بلاگ میں، اسٹیو یگ بیان کرتے ہیں کہ کس طرح جاوا کی دنیا سختی سے اسموں (ہستیوں، اشیاء - تقریباً ترجمہ ) سے منسلک ہے۔ اگر آپ نے اس کا بلاگ نہیں پڑھا ہے تو میں اس کی سفارش کرتا ہوں۔ وہ ایک مضحکہ خیز اور دلچسپ انداز میں اس کی صحیح وجہ بیان کرتا ہے کہ جاوا میں لیمبڈا کے تاثرات کیوں شامل کیے گئے۔ لیمبڈا اظہار جاوا میں فعالیت لاتے ہیں جو اتنے عرصے سے غائب ہے۔ لیمبڈا اظہارات اشیاء کی طرح زبان میں فعالیت لاتے ہیں۔ اگرچہ یہ 100% درست نہیں ہے، آپ دیکھ سکتے ہیں کہ Lambda اظہارات، بند ہونے کے باوجود، اسی طرح کی صلاحیتیں فراہم کرتے ہیں۔ ایک فعال زبان میں، لیمبڈا اظہار افعال ہیں؛ لیکن جاوا میں، لیمبڈا ایکسپریشنز کی نمائندگی آبجیکٹ کے ذریعے کی جاتی ہے، اور اس کا تعلق ایک مخصوص آبجیکٹ کی قسم سے ہونا چاہیے جسے فنکشنل انٹرفیس کہتے ہیں۔ آگے ہم دیکھیں گے کہ یہ کیا ہے۔ ماریو فوسکو کا مضمون "ہمیں جاوا میں لیمبڈا ایکسپریشن کی ضرورت کیوں ہے" میں تفصیل سے بتایا گیا ہے کہ تمام جدید زبانوں کو بند کرنے کی صلاحیتوں کی ضرورت کیوں ہے۔

لیمبڈا اظہار کا تعارف

لیمبڈا اظہار گمنام افعال ہیں (ہو سکتا ہے کہ جاوا کے لیے 100% درست تعریف نہ ہو، لیکن یہ کچھ وضاحت لاتا ہے)۔ سیدھے الفاظ میں، یہ اعلان کے بغیر ایک طریقہ ہے، یعنی رسائی میں ترمیم کرنے والوں کے بغیر، واپسی قدر اور نام۔ مختصر میں، وہ آپ کو ایک طریقہ لکھنے اور اسے فوری طور پر استعمال کرنے کی اجازت دیتے ہیں۔ یہ خاص طور پر ایک وقتی طریقہ کال کے معاملے میں مفید ہے، کیونکہ کلاس بنائے بغیر طریقہ کا اعلان کرنے اور لکھنے میں لگنے والے وقت کو کم کرتا ہے۔ جاوا میں لیمبڈا اظہار میں عام طور پر درج ذیل نحو ہوتا ہے (аргументы) -> (тело)۔ مثال کے طور پر:
(арг1, арг2...) -> { тело }

(тип1 арг1, тип2 арг2...) -> { тело }
ذیل میں حقیقی لیمبڈا اظہار کی کچھ مثالیں ہیں:
(int a, int b) -> {  return a + b; }

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

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

() -> 42

() -> { return 3.1415 };

لیمبڈا اظہار کی ساخت

آئیے لیمبڈا اظہار کی ساخت کا مطالعہ کریں:
  • لیمبڈا ایکسپریشنز میں 0 یا زیادہ ان پٹ پیرامیٹرز ہو سکتے ہیں۔
  • پیرامیٹرز کی قسم کو واضح طور پر بیان کیا جا سکتا ہے یا سیاق و سباق سے حاصل کیا جا سکتا ہے۔ مثال کے طور پر ( int a) کو اس طرح لکھا جا سکتا ہے ( a)
  • پیرامیٹرز قوسین میں بند ہیں اور کوما سے الگ کیے گئے ہیں۔ مثال کے طور پر ( a, b) یا ( int a, int b) یا ( String a, )int bfloat c
  • اگر کوئی پیرامیٹرز نہیں ہیں، تو آپ کو خالی قوسین استعمال کرنے کی ضرورت ہے۔ مثال کے طور پر() -> 42
  • جب صرف ایک پیرامیٹر ہو، اگر قسم واضح طور پر متعین نہ ہو، تو قوسین کو چھوڑا جا سکتا ہے۔ مثال:a -> return a*a
  • لیمبڈا اظہار کے جسم میں 0 یا اس سے زیادہ اظہارات ہوسکتے ہیں۔
  • اگر باڈی ایک ہی بیان پر مشتمل ہے، تو یہ گھوبگھرالی منحنی خطوط وحدانی میں بند نہیں کیا جا سکتا، اور واپسی کی قیمت کلیدی لفظ کے بغیر بتائی جا سکتی ہے return۔
  • بصورت دیگر، گھوبگھرالی منحنی خطوط وحدانی کی ضرورت ہے (کوڈ کا بلاک) اور واپسی کی قدر کو مطلوبہ الفاظ کا استعمال کرتے ہوئے آخر میں بیان کیا جانا چاہیے return(بصورت دیگر واپسی کی قسم ہوگی void

ایک فعال انٹرفیس کیا ہے

جاوا میں، مارکر انٹرفیس ایسے انٹرفیس ہوتے ہیں جن کا اعلان طریقوں یا فیلڈز کے بغیر ہوتا ہے۔ دوسرے الفاظ میں، ٹوکن انٹرفیس خالی انٹرفیس ہیں۔ اسی طرح، فنکشنل انٹرفیس ایسے انٹرفیس ہیں جن پر صرف ایک تجریدی طریقہ کا اعلان کیا گیا ہے۔ java.lang.Runnableفنکشنل انٹرفیس کی ایک مثال ہے۔ یہ صرف ایک طریقہ کا اعلان کرتا ہے void run()۔ ایک انٹرفیس بھی ہے ActionListener- فعال بھی۔ پہلے، ہمیں ایسی اشیاء بنانے کے لیے گمنام کلاسز کا استعمال کرنا پڑتا تھا جو ایک فنکشنل انٹرفیس کو نافذ کرتی ہیں۔ لیمبڈا اظہار کے ساتھ، سب کچھ آسان ہو گیا ہے. ہر لیمبڈا اظہار واضح طور پر کچھ فنکشنل انٹرفیس کا پابند ہوسکتا ہے۔ Runnableمثال کے طور پر، آپ انٹرفیس کا حوالہ بنا سکتے ہیں ، جیسا کہ درج ذیل مثال میں دکھایا گیا ہے۔
Runnable r = () -> System.out.println("hello world");
اس قسم کی تبدیلی ہمیشہ واضح طور پر کی جاتی ہے جب ہم کسی فنکشنل انٹرفیس کی وضاحت نہیں کرتے ہیں:
new Thread(
    () -> System.out.println("hello world")
).start();
مندرجہ بالا مثال میں، مرتب کرنے والا خود بخود کلاس کنسٹرکٹر Runnableسے انٹرفیس کے نفاذ کے طور پر لیمبڈا ایکسپریشن بناتا ہے Thread۔ public Thread(Runnable r) { }یہاں لیمبڈا اظہار اور متعلقہ فنکشنل انٹرفیس کی کچھ مثالیں ہیں:
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 };
@FunctionalInterfaceجاوا 8 میں جاوا لینگویج اسپیسیفیکیشن کے مطابق شامل کردہ تشریح چیک کرتی ہے کہ آیا اعلان کردہ انٹرفیس فعال ہے یا نہیں۔ اس کے علاوہ، Java 8 میں Lambda اظہار کے ساتھ استعمال کے لیے متعدد ریڈی میڈ فنکشنل انٹرفیس شامل ہیں۔ @FunctionalInterfaceاگر اعلان کردہ انٹرفیس فعال نہیں ہے تو تالیف کی غلطی پھینک دے گا۔ مندرجہ ذیل ایک فنکشنل انٹرفیس کی وضاحت کی ایک مثال ہے:
@FunctionalInterface
public interface WorkerInterface {

    public void doSomeWork();

}
جیسا کہ تعریف سے پتہ چلتا ہے، ایک فنکشنل انٹرفیس میں صرف ایک خلاصہ طریقہ ہو سکتا ہے۔ اگر آپ ایک اور تجریدی طریقہ شامل کرنے کی کوشش کرتے ہیں، تو آپ کو تالیف کی خرابی ملے گی۔ مثال:
@FunctionalInterface
public interface WorkerInterface {

    public void doSomeWork();

    public void doSomeMoreWork();

}
خرابی
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") );
    }

}
نتیجہ:
Worker вызван через анонимный класс
Worker вызван через Lambda
یہاں ہم نے اپنے فنکشنل انٹرفیس کی وضاحت کی ہے اور لیمبڈا ایکسپریشن استعمال کیا ہے۔ یہ طریقہ execute()لیمبڈا کے تاثرات کو بطور دلیل قبول کرنے کے قابل ہے۔

لیمبڈا اظہار کی مثالیں۔

لیمبڈا کے تاثرات کو سمجھنے کا بہترین طریقہ چند مثالوں کو دیکھنا ہے: ایک سلسلہ کو Threadدو طریقوں سے شروع کیا جا سکتا ہے:
// 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();
جاوا 8 میں ایونٹ مینجمنٹ لیمبڈا ایکسپریشنز کے ذریعے بھی کیا جا سکتا ہے۔ ایونٹ ہینڈلر کو ActionListenerUI جزو میں شامل کرنے کے دو طریقے درج ذیل ہیں:
// 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!");
});
دی گئی صف کے تمام عناصر کو ظاہر کرنے کی ایک سادہ مثال۔ نوٹ کریں کہ لیمبڈا اظہار کو استعمال کرنے کے ایک سے زیادہ طریقے ہیں۔ ذیل میں ہم تیر نحو کا استعمال کرتے ہوئے معمول کے مطابق ایک لیمبڈا اظہار بناتے ہیں، اور ہم ڈبل کولن آپریٹر بھی استعمال کرتے ہیں (::)، جو جاوا 8 میں ایک باقاعدہ طریقہ کو لیمبڈا اظہار میں تبدیل کرتا ہے:
// 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);
مندرجہ ذیل مثال میں، ہم Predicateایک ٹیسٹ بنانے کے لیے ایک فنکشنل انٹرفیس کا استعمال کرتے ہیں اور اس ٹیسٹ کو پاس کرنے والے آئٹمز کو پرنٹ کرتے ہیں۔ اس طرح آپ لامبڈا اظہار میں منطق ڈال سکتے ہیں اور اس کی بنیاد پر چیزیں کر سکتے ہیں۔
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();
    }

}
نتیجہ:
Выводит все числа: 1 2 3 4 5 6 7
Не выводит ни одного числа:
Вывод четных чисел: 2 4 6
Вывод нечетных чисел: 1 3 5 7
Вывод чисел больше 5: 6 7
لیمبڈا کے تاثرات کے ساتھ ٹنکرنگ کرکے، آپ فہرست کے ہر عنصر کے مربع کو ظاہر کرسکتے ہیں۔ stream()نوٹ کریں کہ ہم ایک باقاعدہ فہرست کو سٹریم میں تبدیل کرنے کا طریقہ استعمال کر رہے ہیں ۔ جاوا 8 ایک زبردست کلاس فراہم کرتا ہے Stream( java.util.stream.Stream) اس میں بہت سارے مفید طریقے ہیں جن کے ساتھ آپ لیمبڈا اظہار استعمال کرسکتے ہیں۔ ہم ایک لیمبڈا اظہار x -> x*xکو طریقہ میں منتقل کرتے ہیں map()، جو اس کا اطلاق سٹریم کے تمام عناصر پر ہوتا ہے۔ جس کے بعد ہم forEachفہرست کے تمام عناصر کو پرنٹ کرنے کے لیے استعمال کرتے ہیں۔
// 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);
ایک فہرست کو دیکھتے ہوئے، آپ کو فہرست کے تمام عناصر کے مربعوں کا مجموعہ پرنٹ کرنے کی ضرورت ہے۔ لیمبڈا اظہارات آپ کو کوڈ کی صرف ایک لائن لکھ کر اسے حاصل کرنے کی اجازت دیتے ہیں۔ یہ مثال convolution (کمی) کا طریقہ استعمال کرتی ہے reduce()۔ ہم map()ہر عنصر کو مربع کرنے کے لیے ایک طریقہ استعمال کرتے ہیں، اور پھر reduce()تمام عناصر کو ایک عدد میں سمیٹنے کے لیے ایک طریقہ استعمال کرتے ہیں۔
// 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);

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

بنیادی فرق کلیدی لفظ کا استعمال ہے this۔ گمنام کلاسز کے لیے، ' ' کلیدی لفظ thisگمنام کلاس کے کسی شے کو ظاہر کرتا ہے، جب کہ لیمبڈا اظہار میں، ' this' کلاس کی ایک شے کو ظاہر کرتا ہے جس میں لیمبڈا اظہار استعمال ہوتا ہے۔ ایک اور فرق ان کے مرتب کرنے کا طریقہ ہے۔ جاوا لیمبڈا اظہار کو مرتب کرتا ہے اور انہیں privateکلاس طریقوں میں تبدیل کرتا ہے۔ یہ invokedynamic انسٹرکشن کا استعمال کرتا ہے ، جو جاوا 7 میں ڈائنامک میتھڈ بائنڈنگ کے لیے متعارف کرایا گیا ہے۔ ٹل ویس نے اپنے بلاگ میں بتایا کہ کس طرح جاوا لیمبڈا اظہار کو بائیک کوڈ میں مرتب کرتا ہے۔

نتیجہ

مارک رین ہولڈ (اوریکل کے چیف آرکیٹیکٹ) نے لیمبڈا ایکسپریشنز کو پروگرامنگ ماڈل میں اب تک کی سب سے اہم تبدیلی قرار دیا - یہاں تک کہ جنرک سے بھی زیادہ اہم۔ وہ درست ہوگا کیونکہ... وہ جاوا پروگرامرز کو فنکشنل پروگرامنگ لینگویج کی صلاحیتیں فراہم کرتے ہیں جس کا ہر کوئی انتظار کر رہا ہے۔ اختراعات جیسے کہ ورچوئل ایکسٹینشن کے طریقوں کے ساتھ، لیمبڈا ایکسپریشنز آپ کو بہت اعلیٰ معیار کا کوڈ لکھنے کی اجازت دیتے ہیں۔ مجھے امید ہے کہ اس مضمون نے آپ کو جاوا 8 کے ہڈ کے نیچے ایک نظر دیا ہے۔ اچھی قسمت :)
تبصرے
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION