وصف بناء الجملة
يتكون تعبير Lambda من الأجزاء التالية:
1. قائمة مفصولة بفاصلة من المعلمات الرسمية بين قوسين. تحتوي طريقة checkperson.test على معلمة p ، والتي تمثل مثيلًا لفئة الشخص. ملاحظة: يمكن حذف أنواع المعلمات في تعبيرات Lambda ؛ بالإضافة إلى ذلك ، إذا كان هناك معلمة واحدة فقط ، يمكن أيضًا حذف قوسين. على سبيل المثال ، الرمز المذكور في القسم السابق:
p -> p.getGender () == person.sex.male && p.getage ()> = 18 && p.getage () <= 25
2. رمز السهم: ->. تستخدم لفصل المعلمات وأجسام الوظائف.
3. وظيفة الجسم. يتكون من تعبير أو كتلة من الكود. في المثال في القسم السابق ، تم استخدام هذا التعبير:
p.getGender () == person.sex.male && p.getage ()> = 18 && p.getage () <= 25
إذا كنت تستخدم تعبيرًا ، فسيقوم وقت تشغيل Java بحساب قيمة التعبير وإرجاعه. بالإضافة إلى ذلك ، يمكنك أيضًا اختيار استخدام عبارة الإرجاع في كتلة الكود:
p -> {return p.getgender () == person.sex.male && p.getage ()> = 18 && p.getage () <= 25 ؛ }ومع ذلك ، فإن بيان العودة ليس تعبيرًا. في تعبيرات Lambda ، يجب إرفاق البيان بأقواس مجعد ، ولكن ليست هناك حاجة إلى إرفاق العبارات في أقواس مجعد عند استدعاء طريقة ذات قيمة إرجاع فارغة ، وبالتالي فإن طريقة الكتابة التالية صحيحة أيضًا:
البريد الإلكتروني -> system.out.println (البريد الإلكتروني)
هناك العديد من أوجه التشابه في إعلان تعبيرات Lambda وطرقها. لذلك ، يمكن أيضًا اعتبار تعبيرات Lambda أساليب مجهولة ، أي طرق بدون تعريف اسم.
تعبيرات Lambda المذكورة أعلاه كلها تعبيرات تستخدم معلمة واحدة فقط كمعلمة رسمية. يوضح الفئة التالية ، Caulater ، كيفية استخدام معلمات متعددة كمعلمات رسمية:
package com.zhyea.zytools ؛ class class public {interface integermath {int operation (int a ، int b) ؛ } public int OperationBinary (int a ، int b ، integermath op) {return op.operation (a ، b) ؛ } public static void main (string ... args) {calculator myapp = new calculator () ؛ إضافة integermath = (a ، b) -> a + b ؛ system.out.println ("40 + 2 =" + myapp.operateBinary (40 ، 2 ، إضافة)) ؛ System.out.println ("20 - 10 =" + myapp.operateBinary (20 ، 10 ، اطرح)) ؛ }}تستخدم طريقة التشغيل في الكود معلمتين صحيحتين لإجراء عمليات حسابية. العملية الحسابية هنا هي في حد ذاتها مثال على واجهة Itegermath. في البرنامج أعلاه ، يتم تعريف عمليتين حسابيتين باستخدام تعبيرات Lambda: الإضافة والطرح. سيقوم تنفيذ البرنامج بطباعة المحتوى التالي:
40 + 2 = 4220 - 10 = 10
الوصول إلى المتغيرات المحلية للفئات الخارجية
على غرار الفئات المحلية أو الفئات المجهولة ، يمكن تعبيرات Lambda أيضًا الوصول إلى المتغيرات المحلية للفئات الخارجية. الفرق هو أنه ليست هناك حاجة للنظر في قضايا مثل التجاوز عند استخدام تعبيرات Lambda. تعبير Lambda هو مجرد مفهوم معجمي ، مما يعني أنه لا يحتاج إلى ورث أي أسماء من الطبقة الفائقة ، ولا يقدم نطاقات جديدة. وهذا يعني أن الإعلان في تعبير Lambda له نفس المعنى مثل الإعلان في بيئته الخارجية. يظهر هذا في المثال التالي:
package com.zhyea.zytools ؛ import java.util.function.consumer ؛ public class lambdascopetest {public int x = 0 ؛ فئة FirstLevel {public int x = 1 ؛ void methodInfirstlevel (int x) {// سوف يتسبب البيان التالي في الإبلاغ عن خطأ في الإبلاغ عن خطأ "يجب أن تكون المتغيرات المحلية المشار إليها من تعبير lambda نهائيًا أو فعالًا" // x = 99 ؛ المستهلك <integer> myconsumer = (y) -> {system.out.println ("x =" + x) ؛ // بيان a system.out.println ("y =" + y) ؛ System.out.println ("this.x =" + this.x) ؛ system.out.println ("lambdascopetest.tis.x =" + lambdascopetest.tis.x) ؛ } ؛ myconsumer.accept (x) ؛ }} public static void main (string ... args) {lambdascopetest st = new lambdascopetest () ؛ lambdascopetest.firstlevel fl = st.new firstlevel () ؛ fl.methodinfirstlevel (23) ؛ }}سيؤدي هذا الرمز إلى إخراج ما يلي:
x = 23y = 23 this.x = 1lambdascopetest.tis.x = 0
إذا استبدلت المعلمة y في تعبير Lambda myconsumer في المثال بـ x ، فسيقوم المترجم بالإبلاغ عن خطأ:
المستهلك <integer> myconsumer = (x) -> {// ....} ؛رسالة خطأ المترجم هي: "تم تعريف المتغير X بالفعل في MethodInfirstlevel (int)" ، مما يعني أنه تم تعريف المتغير x في طريقة methodInfirstlevel. تم الإبلاغ عن خطأ لأن تعبير Lambda لا يقدم نطاقًا جديدًا. لذلك ، يمكنك الوصول مباشرة إلى حقول المجال والأساليب والمعلمات الرسمية للفئة الخارجية في تعبير Lambda. في هذا المثال ، يقوم تعبير Lambda MyConsumer بإدخال المعلمة X مباشرةً من MethodInfirstlevel. عند الوصول إلى أعضاء الفئات الخارجية ، يتم استخدام هذه الكلمة الرئيسية أيضًا مباشرة. في هذا المثال ، يشير this.x إلى firstlevel.x.
ومع ذلك ، مثل الفئات المحلية أو المجهولة ، يمكن تعبيرات Lambda الوصول فقط إلى المتغيرات المحلية أو أعضاء خارجيين تم إعلانها على أنها نهائية (أو مكافئة للنهائي). على سبيل المثال ، نقوم بإزالة التعليق قبل "x = 99" في طريقة رمز methodInfirstlevel طريقة:
// سيؤدي البيان التالي إلى الإبلاغ عن خطأ في الإبلاغ عن خطأ "يجب أن تكون المتغيرات المحلية المشار إليها من تعبير Lambda نهائيًا أو فعليًا" x = 99 ؛ المستهلك <integer> myconsumer = (y) -> {system.out.println ("x =" + x) ؛ // بيان a system.out.println ("y =" + y) ؛ System.out.println ("this.x =" + this.x) ؛ system.out.println ("lambdascopetest.tis.x =" + lambdascopetest.tis.x) ؛ } ؛نظرًا لأن قيمة المعلمة X يتم تعديلها في هذا البيان ، لم يعد من الممكن اعتبار المعلمة X من MethodInfirstlevel نهائيًا. لذلك ، سيقوم برنامج التحويل البرمجي Java بالإبلاغ عن خطأ مثل "المتغيرات المحلية المشار إليها من تعبير Lambda يجب أن تكون نهائية أو فعالة" حيث يصل تعبير Lambda إلى المتغير المحلي x.
نوع الهدف
كيف تحدد نوع تعبير Lambda؟ دعونا نلقي نظرة على رمز تصفية الأفراد العسكريين في سن مناسب:
p -> p.getGender () == person.sex.male && p.getage ()> = 18 && p.getage () <= 25
تم استخدام هذا الرمز في مكانين:
printpersons printpersons الثابتة العامة (قائمة <Person> قائمة ، اختبار CheckPerson) - الحل 3
printpersonspersonswithswith -void (قائمة <Person> ، المسند <Person> tester) - الخطة السادسة
عند استدعاء طريقة PrintPersons ، تتوقع هذه الطريقة معلمة نوع CheckPerson. في هذا الوقت ، التعبير أعلاه هو تعبير نوع checkperson. عند استدعاء طريقة printpersonswithpredicate ، من المتوقع معلمة من النوع <proferation>. في هذا الوقت ، فإن التعبير نفسه هو من نوع المسند <Phone>. مثل هذا ، يسمى النوع الذي تحدده النوع المتوقع بواسطة الطريقة نوع الهدف (في الواقع ، أعتقد أن استنتاج النوع في Scala أكثر ملاءمة هنا). يحدد برنامج التحويل البرمجي Java نوع تعبير Lambda من خلال سياق نوع الهدف أو الموضع عند اكتشاف تعبير Lambda. هذا يعني أنه لا يمكن استخدام تعبيرات Lambda إلا حيث يمكن لمرجم Java استنتاج النوع:
نوع الهدف والمعلمات الطريقة
بالنسبة لمعلمات الطريقة ، يحتاج برنامج التحويل البرمجي Java أيضًا إلى الاعتماد على ميزتين لغتين لتحديد نوع الهدف: تحليل التحميل الزائد واستدلال المعلمة النوع.
ألقِ نظرة على الواجهتين الوظيفيتين التاليتين (java.lang.runnable و java.util.concurrent.callable <v>):
الواجهة العامة Runnable {void run () ؛ } الواجهة العامة قابلة للاتصال <v> {v call () ؛ }لا تُرجع طريقة runnable.run () قيمة ، في حين أن طريقة callable.call () تحتوي عليها.
لنفترض أننا نقوم بتحميل طريقة الاستدعاء مثل ما يلي:
void invoke (runnable r) {r.run () ؛ } <t> t invoke (callable <t> c) {return c.call () ؛ }لذا فإن الطريقة التي سيتم استدعاؤها في البيان التالي:
String s = invoke(() -> "done");
يتم استدعاء الاستدعاء (القابل للاتصال <T>) لأن هذه الطريقة لها قيمة إرجاع ، في حين أن الاستدعاء (Runnable <T>) لا يعود. في هذه الحالة ، يكون نوع تعبير Lambda (() -> "تم") قابل للاتصال <T>.
التسلسل
إذا كان النوع المستهدف لتعبير Lambda وأنواع المعلمات التي يستدعيها قابلة للتسلسل ، فإن تعبير Lambda قابل للتسلسل أيضًا. ومع ذلك ، مثل الطبقات الداخلية ، لا ينصح بقوة تعبيرات Lambda.