คำอธิบายไวยากรณ์
การแสดงออกของแลมบ์ดาประกอบด้วยส่วนต่อไปนี้:
1. รายการที่คั่นด้วยเครื่องหมายจุลภาคของพารามิเตอร์อย่างเป็นทางการในวงเล็บ CheckPerson.Test Method มีพารามิเตอร์ P ซึ่งแสดงถึงอินสแตนซ์ของคลาสบุคคล หมายเหตุ: ประเภทของพารามิเตอร์ในนิพจน์แลมบ์ดาสามารถละเว้นได้; นอกจากนี้หากมีพารามิเตอร์เพียงหนึ่งพารามิเตอร์สามารถละเว้นวงเล็บได้ ตัวอย่างเช่นรหัสที่กล่าวถึงในส่วนก่อนหน้า:
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 Runtime จะคำนวณและส่งคืนค่าของนิพจน์ นอกจากนี้คุณยังสามารถเลือกใช้คำสั่ง Return ในบล็อกรหัส:
p -> {return p.getGender () == person.sex.male && p.getage ()> = 18 && p.getage () <= 25; -อย่างไรก็ตามคำสั่ง Return ไม่ใช่นิพจน์ ในนิพจน์แลมบ์ดาคำสั่งจะต้องถูกปิดล้อมในวงเล็บปีกกา แต่ไม่จำเป็นต้องแนบข้อความในวงเล็บปีกกาเมื่อเรียกวิธีการที่มีค่าคืนที่ว่างเปล่าดังนั้นวิธีการเขียนต่อไปนี้ก็ถูกต้องเช่นกัน:
อีเมล -> system.out.println (อีเมล)
มีความคล้ายคลึงกันมากมายในการประกาศการแสดงออกและวิธีการแลมบ์ดา ดังนั้นการแสดงออกของแลมบ์ดาจึงถือได้ว่าเป็นวิธีที่ไม่ระบุชื่อนั่นคือวิธีการที่ไม่มีนิยามชื่อ
นิพจน์แลมบ์ดาที่กล่าวถึงข้างต้นเป็นนิพจน์ทั้งหมดที่ใช้พารามิเตอร์เดียวเป็นพารามิเตอร์ที่เป็นทางการ คลาสตัวอย่างต่อไปนี้ Caulator แสดงให้เห็นถึงวิธีการใช้พารามิเตอร์หลายตัวเป็นพารามิเตอร์ที่เป็นทางการ:
แพ็คเกจ com.zhyea.zytools; เครื่องคิดเลขคลาสสาธารณะ {อินเตอร์เฟส integermath {การดำเนินการ int (int a, int b); } Public Int Operationbinary (int a, int b, integermath op) {return op.operation (a, b); } โมฆะคงที่สาธารณะหลัก (สตริง ... args) {calculator myApp = ใหม่เครื่องคิดเลข (); การเพิ่มจำนวนมาก = (a, b) -> a + b; System.out.println ("40 + 2 =" + myapp.operatebinary (40, 2, เพิ่มเติม)); System.out.println ("20 - 10 =" + myapp.operateBinary (20, 10, การลบ)); -วิธีการปฏิบัติการ Binary ในรหัสใช้พารามิเตอร์จำนวนเต็มสองตัวเพื่อดำเนินการทางคณิตศาสตร์ การดำเนินการทางคณิตศาสตร์ที่นี่เป็นตัวอย่างของอินเทอร์เฟซ Integermath ในโปรแกรมข้างต้นการดำเนินการทางคณิตศาสตร์สองรายการถูกกำหนดโดยใช้การแสดงออกของแลมบ์ดา: การเพิ่มและการลบ การดำเนินการโปรแกรมจะพิมพ์เนื้อหาต่อไปนี้:
40 + 2 = 4220 - 10 = 10
เข้าถึงตัวแปรท้องถิ่นของคลาสภายนอก
เช่นเดียวกับคลาสท้องถิ่นหรือคลาสที่ไม่ระบุชื่อการแสดงออกของแลมบ์ดายังสามารถเข้าถึงตัวแปรท้องถิ่นของคลาสภายนอก ความแตกต่างคือไม่จำเป็นต้องพิจารณาปัญหาเช่นการเอาชนะเมื่อใช้นิพจน์แลมบ์ดา การแสดงออกของแลมบ์ดาเป็นเพียงแนวคิดคำศัพท์ซึ่งหมายความว่าไม่จำเป็นต้องสืบทอดชื่อใด ๆ จากซูเปอร์คลาสและไม่แนะนำขอบเขตใหม่ นั่นคือการประกาศในการแสดงออกของแลมบ์ดามีความหมายเช่นเดียวกับการประกาศในสภาพแวดล้อมภายนอก สิ่งนี้แสดงให้เห็นในตัวอย่างต่อไปนี้:
แพ็คเกจ com.zhyea.zytools; นำเข้า java.util.function.consumer; Lambdascopetest ระดับสาธารณะ {สาธารณะ int x = 0; ชั้นเรียนระดับแรก {สาธารณะ int x = 1; Void MethodInfirstlevel (int x) {// คำสั่งต่อไปนี้จะทำให้คอมไพเลอร์รายงานข้อผิดพลาด "ตัวแปรท้องถิ่นที่อ้างอิงจากนิพจน์แลมบ์ดาจะต้องเป็นที่สิ้นสุดหรือสุดท้ายได้อย่างมีประสิทธิภาพ" // 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.this.x =" + lambdascopetest.this.x); - myconsumer.accept (x); }} โมฆะคงที่สาธารณะหลัก (สตริง ... args) {lambdascopetest st = ใหม่ lambdascopetest (); lambdascopetest.firstlevel fl = St.New FirstLevel (); Fl.MethodinFirstlevel (23); -รหัสนี้จะส่งออกดังต่อไปนี้:
x = 23y = 23 นี่. x = 1lambdascopetest.this.x = 0
หากคุณแทนที่พารามิเตอร์ y ใน Lambda Expression MyConsumer ในตัวอย่างด้วย X คอมไพเลอร์จะรายงานข้อผิดพลาด:
ผู้บริโภค <Integer> MyConsumer = (x) -> {// .... };ข้อความแสดงข้อผิดพลาดของคอมไพเลอร์คือ: "ตัวแปร X ถูกกำหนดไว้แล้วใน MethodInfirstlevel (int)" ซึ่งหมายความว่าตัวแปร X ได้ถูกกำหนดไว้ในวิธีการ MethodInfirstlevel มีการรายงานข้อผิดพลาดเนื่องจากนิพจน์แลมบ์ดาไม่ได้แนะนำขอบเขตใหม่ ดังนั้นคุณสามารถเข้าถึงฟิลด์โดเมนวิธีการและพารามิเตอร์อย่างเป็นทางการของคลาสภายนอกโดยตรงในนิพจน์แลมบ์ดา ในตัวอย่างนี้ Lambda Expression MyConsumer เข้าถึงพารามิเตอร์ X ของวิธีการ MethodInfirstlevel โดยตรง เมื่อเข้าถึงสมาชิกของคลาสภายนอกคำหลักนี้จะใช้โดยตรง ในตัวอย่างนี้ this.x หมายถึง firstlevel.x
อย่างไรก็ตามเช่นเดียวกับคลาสท้องถิ่นหรือที่ไม่ระบุชื่อการแสดงออกของแลมบ์ดาสามารถเข้าถึงตัวแปรท้องถิ่นหรือสมาชิกภายนอกที่ประกาศว่าเป็นขั้นสุดท้าย (หรือเทียบเท่ากับรอบชิงชนะเลิศ) ตัวอย่างเช่นเราลบความคิดเห็นก่อน "x = 99" ในวิธีการโค้ดตัวอย่างวิธีการใช้วิธีการเรียนรู้:
// คำสั่งต่อไปนี้จะทำให้คอมไพเลอร์รายงานข้อผิดพลาด "ตัวแปรท้องถิ่นที่อ้างอิงจากนิพจน์แลมบ์ดาจะต้องเป็นที่สิ้นสุดหรือสุดท้ายอย่างมีประสิทธิภาพ" 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.this.x =" + lambdascopetest.this.x); -เนื่องจากค่าของพารามิเตอร์ X ถูกแก้ไขในคำสั่งนี้พารามิเตอร์ X ของ MethodInFirstLevel จึงไม่ถือเป็นขั้นสุดท้ายอีกต่อไป ดังนั้นคอมไพเลอร์ Java จะรายงานข้อผิดพลาดเช่น "ตัวแปรท้องถิ่นที่อ้างอิงจากนิพจน์แลมบ์ดาจะต้องเป็นที่สิ้นสุดหรือสุดท้ายอย่างมีประสิทธิภาพ" โดยที่นิพจน์แลมบ์ดาเข้าถึงตัวแปรท้องถิ่น x
ประเภทเป้าหมาย
จะกำหนดประเภทของการแสดงออกของแลมบ์ดาได้อย่างไร? มาดูรหัสสำหรับการกรองบุคลากรทางทหารที่เหมาะสม:
p -> p.getGender () == person.sex.male && p.getage ()> = 18 && p.getage () <= 25
รหัสนี้ใช้ในสองแห่ง:
Public Void Printpersons (รายการ <person> บัญชีรายชื่อผู้ทดสอบ CheckPerson) - โซลูชัน 3
Public Void PrintPersonsWithPredicate (รายการ <person> บัญชีรายชื่อ, predicate <person> tester) - Plan VI
เมื่อเรียกใช้วิธี PrintPersons วิธีนี้จะคาดว่าจะมีพารามิเตอร์ประเภท CheckPerson ในเวลานี้นิพจน์ด้านบนเป็นนิพจน์ประเภทเช็คบ์ เมื่อเรียกใช้วิธี printpersonswithpredicate คาดว่าจะคาดว่าจะมีพารามิเตอร์ประเภท Predicate <person> ในเวลานี้นิพจน์เดียวกันเป็นประเภท Predicate <person> เช่นนี้ประเภทที่กำหนดโดยประเภทที่คาดหวังโดยวิธีนี้เรียกว่าประเภทเป้าหมาย (จริง ๆ แล้วฉันคิดว่าการอนุมานประเภทใน Scala นั้นเหมาะสมกว่าที่นี่) คอมไพเลอร์ Java กำหนดประเภทของนิพจน์แลมบ์ดาผ่านบริบทของประเภทเป้าหมายหรือตำแหน่งเมื่อค้นพบนิพจน์แลมบ์ดา ซึ่งหมายความว่าการแสดงออกของแลมบ์ดาสามารถใช้ได้เฉพาะเมื่อคอมไพเลอร์ Java สามารถอนุมานประเภท:
ประเภทเป้าหมายและพารามิเตอร์วิธีการ
สำหรับพารามิเตอร์วิธีการคอมไพเลอร์ Java ยังต้องพึ่งพาคุณสมบัติสองภาษาเพื่อกำหนดประเภทเป้าหมาย: การแยกวิเคราะห์เกินพิกัดและการอนุมานพารามิเตอร์
ลองดูอินเทอร์เฟซที่ใช้งานได้สองแบบต่อไปนี้ (java.lang.runnable และ java.util.concurrent.callable <v>):
อินเทอร์เฟซสาธารณะ runnable {void run (); } อินเตอร์เฟสสาธารณะ callable <v> {v call (); -วิธี runnable.run () ไม่ส่งคืนค่าในขณะที่วิธี callable.call () มี
สมมติว่าเราโอเวอร์โหลดวิธีการเรียกใช้ดังต่อไปนี้:
โมฆะเรียกใช้ (runnable r) {r.run (); } <t> t เรียกใช้ (callable <t> c) {return c.call (); -ดังนั้นวิธีใดจะถูกเรียกในคำสั่งต่อไปนี้:
String s = invoke(() -> "done");
เรียกใช้ (callable <t>) ถูกเรียกเพราะวิธีนี้มีค่าส่งคืนในขณะที่เรียกใช้ (runnable <t>) ไม่ส่งคืนค่า ในกรณีนี้ประเภทของนิพจน์แลมบ์ดา (() -> "เสร็จสิ้น") สามารถเรียกได้ <t>
การทำให้เป็นอนุกรม
หากประเภทเป้าหมายของนิพจน์แลมบ์ดาและประเภทของพารามิเตอร์ที่เรียกว่าเป็นอนุกรมการแสดงออกของแลมบ์ดาก็สามารถทำให้เป็นอนุกรมได้เช่นกัน อย่างไรก็ตามเช่นเดียวกับชั้นเรียนภายในการทำให้เป็นอนุกรมของการแสดงออกของแลมบ์ดาไม่แนะนำอย่างยิ่ง