مراجعة الواجهات الوظيفية
مراجعة الواجهات الوظيفية
قبل أن تُصبح تعبيرات اللامبدا منطقيةً، تحتاج إلى فهم راسخ لـالواجهات الوظيفية — نظام الأنواع الذي يجعل اللامبدا ممكنةً في Java. يتعمّق هذا الدرس في ماهية الواجهة الوظيفية حقًّا، وسبب حاجة JVM إليها، وكيف يُبقي @FunctionalInterface تصميمك صحيحًا.
ما هي الواجهة الوظيفية؟
الواجهة الوظيفية هي أي واجهة تحتوي على تابع مجرّد واحد بالضبط. هذا التابع المجرّد الوحيد (يُختصر بـSAM) هو "الشكل" الذي يجب أن تملأه اللامبدا. يمكن للواجهة أن تمتلك أي عدد من التوابع الافتراضية والتوابع الساكنة والتوابع الموروثة من java.lang.Object — لكن التوابع المجرّدة فقط هي التي تُحتسب ضمن قيد SAM.
لأنّ Greeter يحتوي على تابع مجرّد واحد فقط، يمكن للمُترجم تعيين أي لامبدا كـ name -> "Hello, " + name عليه مباشرةً دون غموض.
التوصيف @FunctionalInterface
قدّم Java 8 التوصيف @FunctionalInterface. يؤدّي هذا التوصيف وظيفتين:
- توثيق النية — يُشير لكل قارئ بأنّ هذه الواجهة مخصّصة للاستخدام كهدف للامبدا.
- تطبيق قيد SAM في وقت الترجمة — إذا أضفت تابعًا مجرّدًا ثانيًا بالصدفة، يرفض المُترجم الواجهة بدلًا من كسر كل لامبدا تستخدمها بصمت.
لماذا تحتاج Java إلى نوع للامبدا
خلافًا للغات مثل Python أو JavaScript، فإنّ Java محكومة بالأنواع الساكنة: يجب أن يكون لكل تعبير نوع معروف في وقت الترجمة. تعبير اللامبدا وحده — x -> x * 2 — لا يملك نوعًا مستقلًا. يستنتج المُترجم النوع من سياق الهدف: الواجهة الوظيفية التي يُسنَد إليها أو يُمرَّر كوسيطة لها.
يمكن تعيين تعبير اللامبدا نفسه لواجهات وظيفية مختلفة ما دامت توقيعات التوابع متطابقة:
java.util.function.*) على صياغة واجهاتك الخاصة. تأتي مع JDK ثلاث وأربعون واجهة وظيفية جاهزة — Predicate وFunction وConsumer وSupplier وتغييراتها البدائية — وستستكشفها في الدروس القادمة. تعريف @FunctionalInterface مخصّص يكون منطقيًا فقط حين تحتاج اسمًا أكثر وصفيةً أو توقيعًا يتعامل مع الاستثناءات المفحوصة.
ما يُخلّ بقيد SAM
إضافة تابع مجرّد ثانٍ يحوّل الواجهة فورًا إلى واجهة عادية — لا يمكن استخدامها بعد ذلك كهدف للامبدا:
رسالة المُترجم واضحة: "Invalid @FunctionalInterface annotation; Broken is not a functional interface." هنا يُثبت التوصيف قيمته.
التوابع المجرّدة من Object لا تُحتسب
كل فئة Java ترث في نهاية المطاف من Object، لذا فإنّ equals وhashCode وtoString متاحة دائمًا. يمكن لواجهة إعادة تصريح هذه التوابع دون انتهاك قيد SAM:
Object لا تُخلّ بقيد SAM. فقط التوابع المجرّدة الإضافية غير الافتراضية وغير المتعلّقة بـObject تُسبّب المشكلة.
قائمة مرجعية سريعة
- تابع مجرّد واحد بالضبط — وهو SAM. يجب أن تتطابق جميع تعبيرات اللامبدا ومراجع التوابع الموجّهة إلى هذه الواجهة مع توقيعه.
- أي عدد من التوابع الافتراضية والساكنة — تضيف سلوكًا دون كسر العقد.
@FunctionalInterface— يوثّق النية ويوفّر شبكة أمان في وقت الترجمة.- التوصيف غير مطلوب من المُترجم، لكنّه ممارسة جيدة يجب اتباعها باستمرار.
الخلاصة
الواجهة الوظيفية هي الجسر بين نظام الأنواع الساكتة في Java وعالم اللامبدا. تابعها المجرّد الوحيد يُحدّد الشكل الذي يجب أن تتطابق معه كل لامبدا مُسنَدة إليها. يُثبّت التوصيف @FunctionalInterface هذا العقد، فيحوّل إضافة تابع مجرّد ثانٍ بالخطأ إلى خطأ في وقت الترجمة بدلًا من مفاجأة في وقت التشغيل. مع رسوخ هذه الأساسيات، ينتقل الدرس التالي إلى Predicate<T> — الواجهة الوظيفية الأولى في JDK للاختبارات المنطقية.