تعابير لامدا في جافا

مرحبا هبر! أقدم انتباهكم إلى ترجمة مقال "Java Lambda Expressions" لمؤلف www.programiz.com .



المقدمة



في هذه المقالة ، باستخدام الأمثلة ، سوف نستكشف تعبيرات lambda في Java ، واستخدامها مع الواجهات الوظيفية ، والواجهات الوظيفية ذات المعلمات ، و Stream APIs.



تمت إضافة تعبيرات Lambda في Java 8. والغرض الرئيسي منها هو تحسين إمكانية القراءة وتقليل مقدار الشفرة.



لكن قبل الانتقال إلى لامدا ، نحتاج إلى فهم الواجهات الوظيفية.



ما هي الواجهة الوظيفية؟



إذا كانت الواجهة في Java تحتوي على طريقة مجردة واحدة وواحدة فقط ، فإنها تسمى وظيفية. تحدد هذه الطريقة الفردية الغرض من الواجهة.



على سبيل المثال ، تعمل الواجهة Runnable من الحزمة java.lang لأنها تحتوي على طريقة run () واحدة فقط.



مثال 1: التصريح عن واجهة وظيفية في جافا



import java.lang.FunctionalInterface;
@FunctionalInterface
public interface MyInterface{
    //   
    double getValue();
}


في المثال أعلاه ، تحتوي واجهة MyInterface على طريقة مجردة واحدة فقط ، getValue (). لذلك هذه الواجهة وظيفية.



هنا استخدمنا الشرحواجهة وظيفيةمما يساعد المترجم على فهم أن الواجهة تعمل. ومن ثم فهو لا يسمح بأكثر من طريقة مجردة. ومع ذلك ، يمكننا حذفها.



في Java 7 ، تم اعتبار الواجهات الوظيفية أساليب مجردة مفردة (SAM). تم تنفيذ أنظمة SAM عادةً باستخدام فئات مجهولة.



مثال 2: تنفيذ SAM مع فئة مجهولة في java



public class FunctionInterfaceTest {
    public static void main(String[] args) {

        //  
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("      Runnable.")
            }
        }).start();
    }
}


نتيجة التنفيذ:



      Runnable.


في هذا المثال ، نقبل فئة مجهولة لاستدعاء الطريقة. ساعد هذا في كتابة برامج تحتوي على عدد أقل من أسطر التعليمات البرمجية في Java 7. ومع ذلك ، ظلت البنية معقدة للغاية ومرهقة.



قامت Java 8 بتوسيع قدرات SAM لاتخاذ خطوة إلى الأمام. كما نعلم ، تحتوي الواجهة الوظيفية على طريقة واحدة فقط ، لذلك لا نحتاج إلى تحديد اسم الطريقة عند تمريرها كوسيطة. هذا بالضبط ما تسمح لنا به تعابير لامدا.



مقدمة عن تعابير لامدا



تعبيرات لامدا هي أساسًا فئة أو طريقة مجهولة. لا يتم تنفيذ تعبير lambda من تلقاء نفسه. بدلاً من ذلك ، يتم استخدامه لتنفيذ الطريقة المحددة في الواجهة الوظيفية.



كيف أكتب تعبير لامدا في جافا؟



في Java ، تحتوي تعبيرات lambda على النحو التالي:



(parameter list) -> lambda body


هنا استخدمنا عامل تشغيل جديد (->) - عامل تشغيل lambda. ربما يبدو بناء الجملة معقدًا بعض الشيء. لنأخذ بعض الأمثلة.



لنفترض أن لدينا طريقة مثل هذه:



double getPiValue() {
    return 3.1415;
}


يمكننا كتابتها باستخدام لامدا مثل:



() -> 3.1415


هذه الطريقة لا تحتوي على معلمات. لذلك ، يحتوي الجانب الأيسر من التعبير على أقواس فارغة. الجانب الأيمن هو جسم تعبير لامدا ، والذي يحدد عمله. في حالتنا ، القيمة المعادة هي 3.1415.



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



في جافا ، يمكن أن يكون جسم لامدا من نوعين.



1. سطر واحد



() -> System.out.println("Lambdas are great");


2. حظر (متعدد الخطوط)



() -> {
    double pi = 3.1415;
    return pi;
};


يسمح هذا النوع بتعبير لامدا بأن يكون له عمليات متعددة داخليًا. يجب أن تكون هذه العمليات محاطة بأقواس معقوفة متبوعة بفاصلة منقوطة.



ملاحظة: يجب أن تحتوي تعبيرات lambda متعددة الأسطر دائمًا على عبارة إرجاع ، على عكس العبارات أحادية السطر.



مثال 3: تعبير Lambda لنكتب



برنامج Java يقوم بإرجاع Pi باستخدام تعبير lambda.



كما ذكرنا سابقًا ، لا يتم تنفيذ تعبير lambda تلقائيًا. بدلاً من ذلك ، فإنه يشكل تنفيذًا لطريقة مجردة معلن عنها في واجهة وظيفية.



ولذا ، أولاً ، نحتاج إلى وصف الواجهة الوظيفية.



import java.lang.FunctionalInterface;

//  
@FunctionalInterface
interface MyInterface{

    //  
    double getPiValue();
}
public class Main {

    public static void main( String[] args ) {

    //    MyInterface
    MyInterface ref;
    
    // -
    ref = () -> 3.1415;
    
    System.out.println("Value of Pi = " + ref.getPiValue());
    } 
}


نتيجة التنفيذ:



Value of Pi = 3.1415


في المثال أعلاه:



  • لقد أنشأنا واجهة وظيفية ، MyInterface ، والتي تحتوي على طريقة مجردة واحدة ، getPiValue ().
  • داخل الفصل الرئيسي ، أعلنا عن إشارة إلى MyInterface. لاحظ أنه يمكننا التصريح عن مرجع للواجهة ، لكن لا يمكننا إنشاء كائن منه.



    //   
    	MyInterface ref = new myInterface();
    	//  
    	MyInterface ref;
    


  • ثم قمنا بتعيين تعبير لامدا للرابط



    ref = () -> 3.1415;
  • أخيرًا ، أطلقنا على طريقة getPiValue () باستخدام مرجع الواجهة.



    System.out.println("Value of Pi = " + ref.getPiValue());


تعبيرات لامدا مع المعلمات



حتى هذه اللحظة ، أنشأنا تعبيرات lambda بدون أي معلمات. ومع ذلك ، تمامًا مثل الطرق ، يمكن أن تحتوي لامدا على معلمات.



(n) -> (n % 2) == 0


في هذا المثال ، المتغير n داخل الأقواس هو المعلمة التي تم تمريرها إلى تعبير lambda. يأخذ جسم lambda معلمة ويتحقق منها من أجل التكافؤ.



مثال 4: استخدام تعبير لامدا مع معلمات



@FunctionalInterface
interface MyInterface {

    //  
    String reverse(String n);
}

public class Main {

    public static void main( String[] args ) {

        //    MyInterface
        //  - 
        MyInterface ref = (str) -> {

            String result = "";
            for (int i = str.length()-1; i >= 0 ; i--)
            result += str.charAt(i);
            return result;
        };
        //    
        System.out.println("Lambda reversed = " + ref.reverse("Lambda"));
    }

}


نتيجة التنفيذ:



Lambda reversed = adbmaL


واجهة وظيفية ذات معلمات



حتى هذه النقطة ، استخدمنا واجهات وظيفية لا تقبل سوى نوع قيمة واحد. على سبيل المثال:



@FunctionalInterface
interface MyInterface {
    String reverseString(String n);
}


لا تقبل الواجهة الوظيفية أعلاه إلا String وترجع String. ومع ذلك ، يمكننا أن نجعل واجهتنا عامة لاستخدامها مع أي نوع بيانات.



مثال 5: الواجهة ذات المعاملات وتعبيرات Lambda



//  
@FunctionalInterface
interface GenericInterface<T> {

    //  
    T func(T t);
}

public class Main {

    public static void main( String[] args ) {

        //     
        //   String
        //    
        GenericInterface<String> reverse = (str) -> {

            String result = "";
            for (int i = str.length()-1; i >= 0 ; i--)
            result += str.charAt(i);
            return result;
        };

        System.out.println("Lambda reversed = " + reverse.func("Lambda"));

        //     
        //   Integer
        //    
        GenericInterface<Integer> factorial = (n) -> {

            int result = 1;
            for (int i = 1; i <= n; i++)
            result = i * result;
            return result;
        };

        System.out.println("factorial of 5 = " + factorial.func(5));
    }
}


نتيجة التنفيذ:



Lambda reversed = adbmaL
factorial of 5 = 120


في هذا المثال ، أنشأنا واجهة وظيفية ذات معلمات GenericInterface تحتوي على طريقة func () ذات معلمات.



ثم داخل الفصل الرئيسي:



  • GenericInterface <String> reverse - تنشئ ارتباطًا بواجهة تعمل مع String.
  • GenericInterface <Integer> عاملي - ينشئ ارتباطًا بواجهة تعمل مع عدد صحيح.


تعبيرات Lambda و Stream API



يضيف JDK8 حزمة جديدة ، java.util.stream ، والتي تسمح لمطوري جافا بتنفيذ عمليات مثل البحث أو التصفية أو المطابقة أو الدمج أو معالجة المجموعات مثل القوائم.



على سبيل المثال ، لدينا دفق بيانات (في حالتنا ، قائمة سلاسل) ، حيث تحتوي كل سلسلة على اسم البلد ومدينته. الآن يمكننا معالجة تدفق البيانات هذا واختيار مدن نيبال فقط.



للقيام بذلك ، يمكننا استخدام مجموعة من تعبيرات Stream API و lambda.



مثال 6: استخدام Lambdas في Stream API



import java.util.ArrayList;
import java.util.List;

public class StreamMain {

    //  
    static List<String> places = new ArrayList<>();

    //  
    public static List getPlaces(){

        //    
        places.add("Nepal, Kathmandu");
        places.add("Nepal, Pokhara");
        places.add("India, Delhi");
        places.add("USA, New York");
        places.add("Africa, Nigeria");

        return places;
    }

    public static void main( String[] args ) {

        List<String> myPlaces = getPlaces();
        System.out.println("Places from Nepal:");
        
        //  
        myPlaces.stream()
                .filter((p) -> p.startsWith("Nepal"))
                .map((p) -> p.toUpperCase())
                .sorted()
                .forEach((p) -> System.out.println(p));
    }

}


نتيجة التنفيذ:



Places from Nepal:
NEPAL, KATHMANDU
NEPAL, POKHARA


في المثال أعلاه ، لاحظ هذا التعبير:



myPlaces.stream()
        .filter((p) -> p.startsWith("Nepal"))
        .map((p) -> p.toUpperCase())
        .sorted()
        .forEach((p) -> System.out.println(p));


هنا نستخدم طرقًا مثل filter () ، map () ، forEach () من Stream API ، والتي يمكن أن تأخذ lambdas كمعامل.



أيضًا ، يمكننا وصف التعبيرات الخاصة بنا بناءً على البنية الموصوفة أعلاه. سيسمح لنا ذلك بتقليل عدد أسطر الكود.



All Articles