✨ تعبيرات اللامدا (Lambda Expressions) في جافا: تبسيط الكود إلى أقصى حد! 🚀

مرحباً بك في درس جديد ومثير! اليوم سنتعلم واحدة من أهم الإضافات التي أتت مع جافا 8، وهي تعبيرات اللامدا (Lambda Expressions). إذا كنت قد شعرت بأن كتابة الأكواد الخاصة بالواجهات (Interfaces) التي تحتوي على method واحد فقط أمرٌ ممل ويتطلب الكثير من الأسطر، فإعداد اللامدا قد جاء لينقذك! هيا بنا نكتشف هذا العالم المبسط.


🤔 ما هي تعبيرات اللامدا (Lambda Expression)؟

ببساطة شديدة، اللامدا هي طريقة مختصرة جداً لكتابة implementation (التنفيذ) لواجهة تحتوي على method واحد فقط. فبدلاً من كتابة كتل كود طويلة باستخدام الفئات المجهولة (Anonymous Classes)، يمكنك كتابة كل شيء في سطر واحد بطريقة أنيقة وواضحة.

الفكرة الأساسية: اللامدا تتيح لك معاملة الوظيفة (Function) كقيمة، مما يعني يمكنك تمرير كود قابل للتنفيذ كمعامل (argument) لـ method آخر. هذا يشبه إلى حد كبير تمرير متغير، لكنك هنا تمرر "سلوكاً" أو "إجراءً".


📝 الشكل العام لتعبيرة اللامدا

الصيغة الأساسية لكتابة تعبير لامدا هي كالتالي:

(parameters) -> expression

أو

(parameters) -> { statements; }

دعنا نشرح الرموز:

  • (parameters): قائمة المعاملات (parameters) التي يحتاجها الـ method الذي ننفذه. يمكن أن تكون فارغة () أو تحتوي على معامل واحد أو أكثر.
  • ->: هذا السهم هو قلب تعبير اللامدا، ويفصل بين المعاملات وجسم الـ method.
  • expression / { statements; }: هذا هو الجسم الذي ينفذ المهمة المطلوبة.
    • إذا كانت المهمة تستطيع كتابتها في عبارة واحدة (single expression)، يمكنك كتابتها مباشرة دون الحاجة للأقواس المعقوفة {} أو كلمة return.
    • إذا كانت المهمة تتطلب أكثر من خطوة (multiple statements)، يجب وضعها داخل أقواس معقوفة {}، وعندها يجب استخدام كلمة return explicitly إذا كان الـ method يُرجع قيمة.

🔄 المقارنة: الطريقة القديمة (Anonymous Class) vs. الطريقة الحديثة (Lambda)

لنفهم الفرق بشكل عملي، لننفذ واجهة Runnable الشهيرة، التي تحتوي على method واحد هو run().

1. باستخدام الفئة المجهولة (Anonymous Class - الطريقة الطويلة):

Runnable task = new Runnable() {
    @Override
    public void run() {
        System.out.println("Hello from Anonymous Class!");
    }
};
new Thread(task).start();

2. باستخدام تعبير لامدا (Lambda Expression - الطريقة المختصرة):

Runnable lambdaTask = () -> System.out.println("Hello from Lambda!");
new Thread(lambdaTask).start();

أو بشكل أقصر مباشرة داخل الـ Thread:

new Thread(() -> System.out.println("Hello from Lambda!")).start();

الملاحظات:

  • لاحظ كيف اختفى كل من new Runnable(), @Override, واسم الـ method run.
  • اللامدا تعرف تلقائياً أننا ننفذ الـ method run() لأن واجهة Runnable تحتوي على method واحد فقط.
  • الكود أصبح أنظف، أقصر، وأسهل للقراءة.

🧩 أنواع المعاملات في تعبيرات اللامدا

تعتمد كتابة المعاملات على الـ method الذي تنفذه اللامدا.

1. لا يوجد معاملات (()): مثل المثال السابق مع Runnable.

() -> System.out.println("No parameters here!")

2. معامل واحد: لا تحتاج لوضع قوسين حول المعامل الواحد (يمكنك، لكنه اختياري).

// تنفيذ لواجهة تحتوي على method مثل: void greet(String name)
(name) -> System.out.println("Hello, " + name)
// أو بشكل أقصر
name -> System.out.println("Hello, " + name)

3. أكثر من معامل: يجب وضعهم بين قوسين ().

// تنفيذ لواجهة تحتوي على method مثل: int add(int a, int b)
(a, b) -> a + b

🏗️ أين يمكن استخدام تعبيرات اللامدا؟ الشرط الأساسي!

لا يمكنك استخدام اللامدا مع أي واجهة عشوائية. الشرط الأساسي هو أن الواجهة يجب أن تكون "واجهة وظيفية (Functional Interface)".

الواجهة الوظيفية (Functional Interface) هي أي واجهة تحتوي على method مجرد واحد فقط (single abstract method - SAM). يمكنها أن تحتوي على أي عدد من الـ default أو static methods.

أمثلة على واجهات وظيفية معروفة في جافا:

  • Runnable -> run()
  • Comparator<T> -> compare(T o1, T o2)
  • ActionListener -> actionPerformed(ActionEvent e)

يمكنك حتى إنشاء واجهتك الوظيفية الخاصة بك باستخدام annotation @FunctionalInterface (هذا اختياري لكنه موصى به للتوثيق).

@FunctionalInterface
interface MyFunctionalInterface {
    void mySingleMethod(String message);
}

// الاستخدام مع لامدا
MyFunctionalInterface printer = (msg) -> System.out.println("Message: " + msg);
printer.mySingleMethod("Lambda is cool!");

💡 أمثلة عملية بسيطة أخرى

مثال 1: استخدام مع Comparator لفرض قائمة

import java.util.Arrays;
import java.util.List;
import java.util.Collections;

List<String> names = Arrays.asList("Ali", "Sarah", "Mohamed", "Lama");

// الطريقة القديمة مع Anonymous Class
Collections.sort(names, new Comparator<String>() {
    @Override
    public int compare(String s1, String s2) {
        return s1.compareTo(s2);
    }
});

// الطريقة الحديثة مع Lambda (أقصر وأوضح بكثير!)
Collections.sort(names, (s1, s2) -> s1.compareTo(s2));
// أو حتى أقصر باستخدام method reference: Collections.sort(names, String::compareTo);

مثال 2: لامدا مع جسم مكون من أكثر من سطر

// لنفترض واجهة وظيفية تحتوي على: int process(int x, int y)
Calculator processor = (x, y) -> {
    int sum = x + y;
    int result = sum * 10;
    return result; // يجب استخدام return عند وجود { }
};
int finalResult = processor.process(5, 3); // سترجع 80