البرمجة الوظيفية في Java: من الألف إلى الياء
البرمجة الوظيفية (FP) هي نموذج يتم فيه التعامل مع العمليات الحسابية كحساب للدوال الرياضية، مع تجنب تغيير الحالة والبيانات القابلة للتغيير. Java، وهي لغة أمرية تقليديًا، بدأت من الإصدار 8 في دمج عناصر البرمجة الوظيفية بنشاط. يتيح ذلك كتابة كود أكثر إيجازًا وقابلية للتنبؤ وسهولة في الاختبار، خاصة عند العمل مع المجموعات والعمليات غير المتزامنة.
في هذه المقالة، سنقوم بتحليل المفاهيم الأساسية للبرمجة الوظيفية في Java: من تعبيرات لامدا إلى التقنيات المتقدمة للعمل مع Stream API و Optional. ستتعلم كيفية تطبيق هذه الأدوات عمليًا لجعل كودك أنظف وأكثر كفاءة.
1. الأساسيات: تعبيرات لامدا والواجهات الوظيفية
تعبير لامدا هو شكل مختصر لكتابة طريقة (دالة) مجهولة. يسمح بتمرير السلوك كبيانات. الصيغة: (المعلمات) -> الجسم.
الواجهة الوظيفية هي واجهة تحتوي على طريقة مجردة واحدة فقط (SAM — Single Abstract Method). هذه الواجهات هي التي يمكن تنفيذها باستخدام لامدا. الواجهات المضمنة الرئيسية في java.util.function:
Predicate<T>— تستقبل كائن T، وتعيد boolean (طريقةtest).Function<T, R>— تستقبل T، وتعيد R (طريقةapply).Consumer<T>— تستقبل T، ولا تعيد شيئًا (طريقةaccept).Supplier<T>— لا تستقبل شيئًا، وتعيد T (طريقةget).UnaryOperator<T>— حالة خاصة من Function، حيث T و R من نفس النوع.
مثال: فرز قائمة باستخدام Comparator (واجهة وظيفية)
import java.util.*;
public class LambdaExample { public static void main(String[] args) { List<String> names = Arrays.asList("إيفان", "آنا", "بيتر", "ماريا");
// بدون لامدا (فئة مجهولة) Collections.sort(names, new Comparator<String>() { @Override public int compare(String a, String b) { return a.length() - b.length(); } });
// مع لامدا Collections.sort(names, (a, b) -> a.length() - b.length());
// بشكل أقصر: استخدام Comparator.comparingInt names.sort(Comparator.comparingInt(String::length));
System.out.println(names); // [آنا, بيتر, إيفان, ماريا] }}مثال: استخدام Predicate للتصفية
import java.util.function.Predicate;
public class PredicateExample { public static void main(String[] args) { Predicate<Integer> isEven = n -> n % 2 == 0; Predicate<Integer> isPositive = n -> n > 0;
System.out.println(isEven.test(4)); // true System.out.println(isEven.and(isPositive).test(-2)); // false (ليس موجبًا) System.out.println(isEven.or(isPositive).test(-3)); // true (ليس زوجيًا، ولكنه موجب؟ لا، -3 ليس موجبًا) }}2. Stream API: أداة قوية لمعالجة البيانات
Stream API هو تسلسل من العناصر يدعم العمليات المتوازية والمتسلسلة. Stream لا يخزن البيانات، بل يحولها. تنقسم العمليات الرئيسية إلى عمليات وسيطة (تعيد Stream جديدًا) و عمليات نهائية (تنهي المعالجة وتعيد نتيجة أو تأثيرًا جانبيًا).
العمليات الأساسية لـ Stream API:
- الوسيطة:
filter,map,flatMap,sorted,distinct,peek,limit,skip. - النهائية:
collec