บทความนี้เหมาะกับใคร?
- สำหรับผู้ที่คิดว่าตนรู้จัก Java Core ดีอยู่แล้ว แต่ไม่มีความรู้เกี่ยวกับ lambda expression ใน Java หรือบางทีคุณอาจเคยได้ยินบางอย่างเกี่ยวกับแลมบ์ดามาแล้วแต่ไม่มีรายละเอียด
- สำหรับผู้ที่มีความเข้าใจเกี่ยวกับสำนวนแลมบ์ดาบ้าง แต่ยังกลัวและใช้งานไม่ปกติ
หากคุณไม่ได้อยู่ในหมวดหมู่เหล่านี้ คุณอาจพบว่าบทความนี้น่าเบื่อ ไม่ถูกต้อง และโดยทั่วไปแล้ว "ไม่เจ๋ง" ในกรณีนี้ อย่าลังเลที่จะผ่านไป หรือหากคุณเชี่ยวชาญหัวข้อนี้เป็นอย่างดี ให้เสนอความคิดเห็นในความคิดเห็นว่าฉันจะปรับปรุงหรือเสริมบทความได้อย่างไร เนื้อหาไม่ได้อ้างคุณค่าทางวิชาการใด ๆ มีความแปลกใหม่น้อยกว่ามาก ในทางกลับกัน: ในนั้นฉันจะพยายามอธิบายสิ่งที่ซับซ้อน (สำหรับบางคน) ให้ง่ายที่สุดเท่าที่จะทำได้ ฉันได้รับแรงบันดาลใจในการเขียนโดยขอให้อธิบายสตรีม API ฉันคิดเกี่ยวกับมันและตัดสินใจว่าหากไม่เข้าใจสำนวนแลมบ์ดา ตัวอย่างบางส่วนของฉันเกี่ยวกับ "สตรีม" ก็คงไม่สามารถเข้าใจได้ เริ่มจากแลมบ์ดากันก่อน
ความรู้อะไรที่จำเป็นในการทำความเข้าใจบทความนี้:
- ความเข้าใจในการเขียนโปรแกรมเชิงวัตถุ (ต่อไปนี้จะเรียกว่า OOP) ได้แก่ :
- ความรู้เกี่ยวกับคลาสและวัตถุคืออะไรความแตกต่างระหว่างสิ่งเหล่านี้คืออะไร
- ความรู้เกี่ยวกับอินเทอร์เฟซคืออะไร แตกต่างจากคลาสอย่างไร การเชื่อมต่อระหว่างอินเทอร์เฟซและคลาสคืออะไร (อินเทอร์เฟซและคลาส)
- ความรู้ว่าวิธีการคืออะไร เรียกมันอย่างไร วิธีนามธรรมคืออะไร (หรือวิธีการที่ไม่มีการดำเนินการ) พารามิเตอร์/ข้อโต้แย้งของวิธีการคืออะไร วิธีส่งผ่านวิธีการเหล่านั้น
- ตัวดัดแปลงการเข้าถึง วิธีการ/ตัวแปรแบบสแตติก วิธีการ/ตัวแปรขั้นสุดท้าย
- การสืบทอด (คลาส, อินเทอร์เฟซ, การสืบทอดหลายอินเทอร์เฟซ)
- ความรู้เกี่ยวกับ Java Core: ข้อมูลทั่วไป, คอลเลกชัน (รายการ), เธรด
เอาล่ะ มาเริ่มกันเลย
ประวัติเล็กน้อย
นิพจน์ Lambda มาจาก Java จากการเขียนโปรแกรมเชิงฟังก์ชัน และจากคณิตศาสตร์ ในช่วงกลางศตวรรษที่ 20 ในอเมริกา โบสถ์อลอนโซแห่งหนึ่งทำงานที่มหาวิทยาลัยพรินซ์ตัน ซึ่งชื่นชอบคณิตศาสตร์และนามธรรมทุกประเภทมาก คริสตจักรอลอนโซเป็นผู้คิดค้นแคลคูลัสแลมบ์ดา ซึ่งในตอนแรกเป็นชุดของแนวคิดเชิงนามธรรมบางอย่าง และไม่เกี่ยวข้องกับการเขียนโปรแกรมเลย ในเวลาเดียวกัน นักคณิตศาสตร์เช่น Alan Turing และ John von Neumann ทำงานที่มหาวิทยาลัยพรินซ์ตันแห่งเดียวกัน ทุกอย่างมารวมกัน: Church คิดค้นระบบแคลคูลัสแลมบ์ดา ทัวริงได้พัฒนาเครื่องคำนวณเชิงนามธรรมของเขา ซึ่งปัจจุบันรู้จักกันในชื่อ "เครื่องทัวริง" ฟอน นอยมันน์ได้เสนอแผนภาพสถาปัตยกรรมของคอมพิวเตอร์ ซึ่งเป็นพื้นฐานของคอมพิวเตอร์สมัยใหม่ (และปัจจุบันเรียกว่า "สถาปัตยกรรมของฟอน นอยมันน์") ในเวลานั้น แนวคิดของโบสถ์อลอนโซไม่ได้รับชื่อเสียงมากเท่ากับงานของเพื่อนร่วมงานของเขา (ยกเว้นสาขาคณิตศาสตร์ที่ "บริสุทธิ์") อย่างไรก็ตาม หลังจากนั้นไม่นาน John McCarthy บางคน (ซึ่งสำเร็จการศึกษาจากมหาวิทยาลัยพรินซ์ตันในช่วงเวลาของเรื่องซึ่งเป็นพนักงานของสถาบันเทคโนโลยีแมสซาชูเซตส์) เริ่มสนใจแนวคิดของ Church จากพื้นฐานเหล่านี้ ในปี 1958 เขาได้สร้างภาษาการเขียนโปรแกรมเชิงฟังก์ชันภาษาแรก Lisp และ 58 ปีต่อมา แนวคิดเกี่ยวกับการเขียนโปรแกรมเชิงฟังก์ชันรั่วไหลเข้าสู่ Java ในอันดับที่ 8 ผ่านไปไม่ถึง 70 ปี... อันที่จริง นี่ไม่ใช่ระยะเวลาที่ยาวนานที่สุดในการนำแนวคิดทางคณิตศาสตร์ไปใช้ในทางปฏิบัติ
สาระการเรียนรู้แกนกลาง
นิพจน์แลมบ์ดาก็เป็นฟังก์ชันเช่นนี้ คุณสามารถคิดว่านี่เป็นวิธีการปกติใน Java ข้อแตกต่างเพียงอย่างเดียวคือสามารถส่งผ่านไปยังวิธีอื่นเป็นอาร์กิวเมนต์ได้ ใช่ มันเป็นไปได้ที่จะส่งผ่านไม่เพียงแต่ตัวเลข สตริง และ cat ไปยังเมธอด แต่ยังรวมถึงวิธีอื่นด้วย! เมื่อใดที่เราต้องการสิ่งนี้? เช่น ถ้าเราต้องการให้โทรกลับบ้าง เราต้องการวิธีการที่เราเรียกเพื่อให้สามารถเรียกวิธีอื่นที่เราส่งผ่านไปได้ นั่นคือเพื่อให้เรามีโอกาสที่จะส่งการโทรกลับครั้งหนึ่งในบางกรณีและอีกการโทรกลับในบางกรณี และวิธีการของเราซึ่งจะยอมรับการโทรกลับของเราก็จะเรียกพวกเขา ตัวอย่างง่ายๆ คือการเรียงลำดับ สมมติว่าเราเขียนการเรียงลำดับที่ซับซ้อนซึ่งมีลักษณะดังนี้:
public void mySuperSort() {
if(compare(obj1, obj2) > 0)
}
โดยที่
if
เราเรียกเมธอด
compare()
ว่า ส่งวัตถุสองชิ้นที่เราเปรียบเทียบไปที่นั่น และเราต้องการค้นหาว่าวัตถุใดที่ "ใหญ่กว่า" เราจะใส่อันที่ "มากกว่า" ก่อนอันที่ "เล็กกว่า" ฉันเขียน "เพิ่มเติม" ในเครื่องหมายคำพูดเพราะเรากำลังเขียนวิธีการสากลที่ไม่เพียงแต่สามารถเรียงลำดับจากน้อยไปมากเท่านั้น แต่ยังเรียงลำดับจากมากไปน้อยด้วย (ในกรณีนี้ "มากกว่า" จะเป็นวัตถุที่เล็กกว่าโดยพื้นฐานแล้ว และในทางกลับกัน) . เพื่อกำหนดกฎสำหรับวิธีที่เราต้องการจัดเรียง เราต้องส่งต่อกฎนั้นไปยังไฟล์
mySuperSort()
. ในกรณีนี้ เราจะสามารถ “ควบคุม” วิธีการของเราในขณะที่มันถูกเรียกได้ แน่นอน คุณสามารถเขียนวิธีแยกกันสองวิธี
mySuperSortAsc()
สำหรับ
mySuperSortDesc()
การเรียงลำดับจากน้อยไปหามากและจากมากไปหาน้อย หรือส่งพารามิเตอร์บางตัวภายในเมธอด (เช่น
boolean
ถ้า
true
เรียงลำดับจากน้อยไปหามาก และหาก
false
เรียงลำดับจากมากไปน้อย) แต่จะเป็นอย่างไรถ้าเราไม่ต้องการเรียงลำดับโครงสร้างที่ไม่ธรรมดา แต่ยกตัวอย่าง รายการอาร์เรย์สตริงล่ะ? วิธีการของเราจะ
mySuperSort()
รู้วิธีจัดเรียงอาร์เรย์สตริงเหล่านี้ได้ อย่างไร ถึงขนาด? ตามความยาวของคำทั้งหมด? อาจจะเรียงตามตัวอักษร ขึ้นอยู่กับแถวแรกในอาร์เรย์? แต่จะเกิดอะไรขึ้นหากในบางกรณี เราต้องเรียงลำดับรายการอาร์เรย์ตามขนาดของอาร์เรย์ และในอีกกรณีหนึ่ง ตามความยาวรวมของคำในอาร์เรย์ ฉันคิดว่าคุณเคยได้ยินเกี่ยวกับตัวเปรียบเทียบแล้ว และในกรณีเช่นนี้ เราก็เพียงส่งออบเจ็กต์ตัวเปรียบเทียบไปยังวิธีการเรียงลำดับของเรา ซึ่งเราจะอธิบายกฎเกณฑ์ที่เราต้องการเรียงลำดับ เนื่องจากมีการใช้วิธีมาตรฐาน
sort()
บนหลักการเดียวกันกับ
mySuperSort()
ในตัวอย่าง ฉันจะใช้วิธี
sort()
มาตรฐาน
String[] array1 = {"Mother", "soap", "frame"};
String[] array2 = {"I", "Very", "I love", "java"};
String[] array3 = {"world", "work", "May"};
List<String[]> arrays = new ArrayList<>();
arrays.add(array1);
arrays.add(array2);
arrays.add(array3);
Comparator<String[]> sortByLength = new Comparator<String[]>() {
@Override
public int compare(String[] o1, String[] o2) {
return o1.length - o2.length;
}
};
Comparator<String[]> sortByWordsLength = new Comparator<String[]>() {
@Override
public int compare(String[] o1, String[] o2) {
int length1 = 0;
int length2 = 0;
for (String s : o1) {
length1 += s.length();
}
for (String s : o2) {
length2 += s.length();
}
return length1 - length2;
}
};
arrays.sort(sortByLength);
ผลลัพธ์:
- แม่ล้างกรอบ
- สันติภาพแรงงานอาจ
- ฉันรักจาวาจริงๆ
ที่นี่อาร์เรย์จะถูกจัดเรียงตามจำนวนคำในแต่ละอาร์เรย์ อาร์เรย์ที่มีคำน้อยกว่าจะถือว่า "เล็กกว่า" นั่นเป็นสาเหตุที่มันมาตอนเริ่มต้น คำที่มีคำมากกว่านั้นถือว่า "มากกว่า" และจบลงที่ตอนท้าย หาก
sort()
เราส่งตัวเปรียบเทียบอื่น ไปยัง method
(sortByWordsLength)
ผลลัพธ์จะแตกต่างออกไป
:
- สันติภาพแรงงานอาจ
- แม่ล้างกรอบ
- ฉันรักจาวาจริงๆ
ตอนนี้อาร์เรย์จะถูกจัดเรียงตามจำนวนตัวอักษรทั้งหมดในคำของอาร์เรย์ดังกล่าว ในกรณีแรกมีตัวอักษร 10 ตัวใน 12 ตัวที่สองและใน 15 ตัวที่สาม หากเราใช้ตัวเปรียบเทียบเพียงตัวเดียว เราจะไม่สามารถสร้างตัวแปรแยกต่างหากสำหรับมันได้ แต่เพียงแค่สร้างอ็อบเจ็กต์ของคลาสที่ไม่ระบุชื่อตรงตำแหน่ง เวลาที่เรียกวิธี
sort()
การ เช่นนั้น:
String[] array1 = {"Mother", "soap", "frame"};
String[] array2 = {"I", "Very", "I love", "java"};
String[] array3 = {"world", "work", "May"};
List<String[]> arrays = new ArrayList<>();
arrays.add(array1);
arrays.add(array2);
arrays.add(array3);
arrays.sort(new Comparator<String[]>() {
@Override
public int compare(String[] o1, String[] o2) {
return o1.length - o2.length;
}
});
ผลลัพธ์จะเหมือนกับในกรณีแรก ภารกิจที่ 1 เขียนตัวอย่างนี้ใหม่เพื่อที่จะเรียงลำดับอาร์เรย์ไม่เรียงลำดับตามจำนวนคำในอาร์เรย์จากน้อยไปหามาก แต่เรียงลำดับจากมากไปน้อย เรารู้ทั้งหมดนี้แล้ว เรารู้วิธีส่งผ่านวัตถุไปยังวิธีการ เราสามารถส่งสิ่งนี้หรือวัตถุนั้นไปยังวิธีการได้ ขึ้นอยู่กับสิ่งที่เราต้องการในขณะนี้ และภายในวิธีที่เราส่งผ่านวัตถุนั้น วิธีการที่เราเขียนการดำเนินการจะถูกเรียกว่า . คำถามเกิดขึ้น: นิพจน์แลมบ์ดาเกี่ยวอะไรกับมัน?
เนื่องจากแลมบ์ดาเป็นวัตถุที่มีวิธีเดียวเท่านั้น มันเหมือนกับวัตถุวิธีการ วิธีการห่อในวัตถุ พวกเขามีไวยากรณ์ที่ผิดปกติเล็กน้อย (แต่จะเพิ่มเติมในภายหลัง) ลองมาดูที่รายการนี้อีกครั้ง
arrays.sort(new Comparator<String[]>() {
@Override
public int compare(String[] o1, String[] o2) {
return o1.length - o2.length;
}
});
ที่นี่เรานำรายการของเรา
arrays
และเรียกวิธีการของมัน
sort()
โดยที่เราส่งวัตถุตัวเปรียบเทียบด้วยวิธีเดียว
compare()
(มันไม่สำคัญสำหรับเราว่ามันเรียกว่าอะไร เพราะมันเป็นสิ่งเดียวในวัตถุนี้ เราจะไม่พลาด) วิธีการนี้รับพารามิเตอร์สองตัว ซึ่งเราจะใช้งานต่อไป หากคุณทำงานใน
IntelliJ IDEAคุณอาจเห็นว่ามันเสนอรหัสนี้ให้คุณสั้นลงอย่างมากได้อย่างไร:
arrays.sort((o1, o2) -> o1.length - o2.length);
นั่นคือวิธีที่หกบรรทัดกลายเป็นหนึ่งบรรทัดสั้น ๆ 6 บรรทัดถูกเขียนใหม่เป็นบรรทัดสั้นหนึ่งบรรทัด มีบางอย่างหายไป แต่ฉันรับประกันว่าไม่มีอะไรสำคัญหายไป และโค้ดนี้จะทำงานได้เหมือนกับคลาสที่ไม่ระบุชื่อทุกประการ
ภารกิจที่ 2 หาวิธีเขียนวิธีแก้ปัญหา 1 ใหม่โดยใช้ lambdas (เป็นทางเลือกสุดท้าย ขอให้
IntelliJ IDEAเปลี่ยนคลาสที่ไม่ระบุชื่อของคุณให้เป็น lambda)
พูดคุยเกี่ยวกับอินเทอร์เฟซ
โดยพื้นฐานแล้วอินเทอร์เฟซเป็นเพียงรายการวิธีการเชิงนามธรรม เมื่อเราสร้างคลาสและบอกว่าจะใช้อินเทอร์เฟซบางประเภท เราต้องเขียนในชั้นเรียนของเราถึงการใช้งานวิธีการที่ระบุไว้ในอินเทอร์เฟซ (หรือเป็นทางเลือกสุดท้าย ไม่ใช่เขียน แต่ทำให้คลาสเป็นนามธรรม ). มีอินเทอร์เฟซที่มีวิธีการที่แตกต่างกันมากมาย (เช่น
List
) มีอินเทอร์เฟซที่มีเพียงวิธีเดียวเท่านั้น (เช่น Comparator หรือ Runnable เดียวกัน) มีอินเทอร์เฟซต่างๆ ที่ไม่มีวิธีเดียวเลย (เรียกว่าอินเทอร์เฟซแบบมาร์กเกอร์ เช่น Serializable) อินเทอร์เฟซที่มีเพียงวิธีเดียวเรียกอีกอย่างว่า
อินเทอร์เฟซที่ใช้งานได้ ใน Java 8 พวกเขาจะถูกทำเครื่องหมายด้วย คำอธิบาย ประกอบ พิเศษ
@FunctionalInterface เป็นการเชื่อมต่อกับวิธีเดียวที่เหมาะสำหรับการใช้นิพจน์แลมบ์ดา ดังที่ฉันได้กล่าวไว้ข้างต้น นิพจน์แลมบ์ดาเป็นวิธีการที่รวมอยู่ในวัตถุ และเมื่อเราส่งวัตถุดังกล่าวไปที่ไหนสักแห่ง อันที่จริง เราก็ผ่านวิธีเดียวนี้ไป ปรากฎว่ามันไม่สำคัญสำหรับเราว่าวิธีนี้เรียกว่าอะไร สิ่งที่สำคัญสำหรับเราคือพารามิเตอร์ที่เมธอดนี้ใช้ และอันที่จริงแล้วคือโค้ดของเมธอดเอง โดยพื้นฐานแล้วนิพจน์แลมบ์ดาคือ การใช้อินเทอร์เฟซการทำงาน เมื่อเราเห็นอินเทอร์เฟซด้วยวิธีเดียว นั่นหมายความว่าเราสามารถเขียนคลาสที่ไม่ระบุชื่อดังกล่าวใหม่ได้โดยใช้แลมบ์ดา หากอินเทอร์เฟซมีมากกว่าหนึ่งหรือน้อยกว่าหนึ่งวิธี นิพจน์แลมบ์ดาจะไม่เหมาะกับเรา และเราจะใช้คลาสที่ไม่ระบุชื่อ หรือแม้แต่คลาสปกติ ถึงเวลาที่จะเจาะเข้าไปในแลมบ์ดา :)
ไวยากรณ์
ไวยากรณ์ทั่วไปเป็นดังนี้:
(параметры) -> {тело метода}
นั่นคือวงเล็บซึ่งอยู่ข้างในคือพารามิเตอร์ของวิธีการ "ลูกศร" (ซึ่งเป็นอักขระสองตัวในแถว: ลบและมากกว่า) หลังจากนั้นเนื้อหาของวิธีการจะอยู่ในเครื่องหมายปีกกาเช่นเคย พารามิเตอร์สอดคล้องกับพารามิเตอร์ที่ระบุในอินเทอร์เฟซเมื่ออธิบายวิธีการ หากคอมไพลเลอร์สามารถกำหนดประเภทของตัวแปรได้อย่างชัดเจน (ในกรณีของเรา เป็นที่ทราบกันดีอยู่แล้วว่าเรากำลังทำงานกับอาร์เรย์ของสตริง เนื่องจากมันถูก
List
พิมพ์อย่างแม่นยำด้วยอาร์เรย์ของสตริง) ดังนั้นประเภทของตัวแปร
String[]
ไม่จำเป็นต้อง ถูกเขียน
หากคุณไม่แน่ใจ ให้ระบุประเภท แล้ว IDEA จะเน้นเป็นสีเทาหากไม่จำเป็น |
คุณสามารถอ่านเพิ่มเติมได้ใน
บทช่วยสอนของ Oracleเป็นต้น สิ่งนี้เรียกว่า"
การพิมพ์เป้าหมาย" คุณสามารถตั้งชื่อตัวแปรใดๆ ก็ได้ ไม่จำเป็นต้องระบุในอินเทอร์เฟซ หากไม่มีพารามิเตอร์ ก็ให้ใส่เพียงวงเล็บเท่านั้น หากมีพารามิเตอร์เพียงตัวเดียว ให้ระบุเพียงชื่อตัวแปรที่ไม่มีวงเล็บ เราได้จัดเรียงพารามิเตอร์แล้ว ตอนนี้เกี่ยวกับเนื้อความของนิพจน์แลมบ์ดาเอง ภายในวงเล็บปีกกา ให้เขียนโค้ดเหมือนกับวิธีปกติ หากโค้ดทั้งหมดของคุณมีเพียงบรรทัดเดียว คุณไม่จำเป็นต้องเขียนวงเล็บปีกกาเลย (เช่นเดียวกับ ifs และ loops) หากแลมบ์ดาของคุณส่งคืนบางสิ่ง แต่เนื้อหาของมันประกอบด้วยหนึ่งบรรทัดก็
return
ไม่จำเป็นต้องเขียนเลย
return
แต่ถ้าคุณมีเครื่องหมายปีกกา คุณจะต้องเขียน . . อย่างชัดเจน เช่นเดียวกับวิธีปกติ
ตัวอย่าง
ตัวอย่างที่ 1
() -> {}
ตัวเลือกที่ง่ายที่สุด และไร้ความหมายที่สุด :) เพราะมันทำอะไรไม่ได้เลย
ตัวอย่างที่ 2
() -> ""
ก็เป็นตัวเลือกที่น่าสนใจเช่นกัน ไม่ยอมรับสิ่งใดและส่งกลับสตริงว่าง (
return
ละเว้นว่าไม่จำเป็น) เหมือนกัน แต่มี
return
:
() -> {
return "";
}
ตัวอย่างที่ 3 สวัสดีชาวโลกโดยใช้แลมบ์ดา
() -> System.out.println("Hello world!")
รับอะไรเลย ไม่ส่งคืนอะไรเลย (เราไม่สามารถใส่
return
ก่อนการโทรได้
System.out.println()
เนื่องจากประเภทการส่งคืนในเมธอด
println() — void)
เพียงแสดงคำจารึกบนหน้าจอ เหมาะอย่างยิ่งสำหรับการใช้งานอินเทอร์เฟ
Runnable
ซ ตัวอย่างเดียวกันนี้สมบูรณ์กว่า:
public class Main {
public static void main(String[] args) {
new Thread(() -> System.out.println("Hello world!")).start();
}
}
หรือเช่นนี้:
public class Main {
public static void main(String[] args) {
Thread t = new Thread(() -> System.out.println("Hello world!"));
t.start();
}
}
หรือคุณสามารถบันทึกนิพจน์แลมบ์ดาเป็นวัตถุประเภท
Runnable
แล้วส่งต่อไปยังตัวสร้าง
thread’а
:
public class Main {
public static void main(String[] args) {
Runnable runnable = () -> System.out.println("Hello world!");
Thread t = new Thread(runnable);
t.start();
}
}
มาดูช่วงเวลาของการบันทึกนิพจน์แลมบ์ดาลงในตัวแปรกันดีกว่า อินเทอร์เฟซ บอกเราว่า วัตถุ
Runnable
ต้องมีเมธอด
public void run()
ตามอินเทอร์เฟซ วิธีการ run ไม่ยอมรับสิ่งใดเป็นพารามิเตอร์ และมันไม่คืนอะไร
(void)
เลย ดังนั้นเมื่อเขียนในลักษณะนี้วัตถุจะถูกสร้างขึ้นด้วยวิธีการบางอย่างที่ไม่ยอมรับหรือส่งคืนสิ่งใด ซึ่งค่อนข้างสอดคล้องกับวิธีการใน
run()
อินเทอร์เฟส
Runnable
นั่นเป็นสาเหตุที่เราสามารถใส่นิพจน์แลมบ์ดานี้ลงในตัวแปร
Runnable
เช่น
ตัวอย่างที่ 4
() -> 42
ขอย้ำอีกครั้งว่ามันไม่ยอมรับสิ่งใดเลย แต่จะส่งคืนตัวเลข 42 นิพจน์แลมบ์ดานี้สามารถวางไว้ในตัวแปรประเภท
Callable
เนื่องจากอินเทอร์เฟซนี้กำหนดเพียงวิธีเดียวเท่านั้น ซึ่งมีลักษณะดังนี้:
V call(),
V
ประเภทของค่าตอบแทนอยู่ ที่ไหน (ในกรณีของเรา
int
) ดังนั้นเราจึงสามารถเก็บนิพจน์แลมบ์ดาได้ดังนี้:
Callable<Integer> c = () -> 42;
ตัวอย่างที่ 5. แลมบ์ดาในหลายบรรทัด
() -> {
String[] helloWorld = {"Hello", "world!"};
System.out.println(helloWorld[0]);
System.out.println(helloWorld[1]);
}
นี่เป็นนิพจน์แลมบ์ดาที่ไม่มีพารามิเตอร์และประเภทการส่งคืน
void
(เนื่องจากไม่มี
return
)
ตัวอย่างที่ 6
x -> x
ที่นี่เรานำบางสิ่งไปเป็นตัวแปร
х
แล้วส่งคืน โปรดทราบว่าหากยอมรับเพียงพารามิเตอร์เดียว ก็ไม่จำเป็นต้องเขียนวงเล็บล้อมรอบพารามิเตอร์นั้น เหมือนกัน แต่มีวงเล็บ:
(x) -> x
และนี่คือตัวเลือกที่มีความชัดเจน
return
:
x -> {
return x;
}
หรือเช่นนี้ด้วยวงเล็บและ
return
:
(x) -> {
return x;
}
หรือระบุประเภทอย่างชัดเจน (และตามด้วยวงเล็บ):
(int x) -> x
ตัวอย่างที่ 7
x -> ++x
เรายอมรับ
х
และคืน แต่สำหรับ
1
มากกว่านั้น คุณยังสามารถเขียนใหม่ได้ดังนี้:
x -> x + 1
ในทั้งสองกรณี เราไม่ได้ระบุวงเล็บล้อมรอบพารามิเตอร์ เนื้อหาของวิธีการ และคำ
return
เนื่องจากไม่จำเป็น ตัวเลือกที่มีวงเล็บเหลี่ยมและส่งคืนอธิบายไว้ในตัวอย่างที่ 6
ตัวอย่างที่ 8
(x, y) -> x % y
เรายอมรับบางส่วน
х
และ ส่งคืนส่วนที่ เหลือ
у
ของการหาร
x
ด้วย
y
จำเป็นต้องมีวงเล็บรอบพารามิเตอร์ที่นี่แล้ว เป็นทางเลือกเฉพาะเมื่อมีเพียงพารามิเตอร์เดียวเท่านั้น เช่นนี้โดยมีข้อบ่งชี้ประเภทที่ชัดเจน:
(double x, int y) -> x % y
ตัวอย่างที่ 9
(Cat cat, String name, int age) -> {
cat.setName(name);
cat.setAge(age);
}
เรายอมรับวัตถุ Cat ซึ่งเป็นสตริงที่มีชื่อและอายุจำนวนเต็ม ในวิธีการนั้น เราได้ตั้งชื่อและอายุที่ส่งผ่านให้กับ Cat เนื่องจาก ตัวแปร
cat
ของเรา เป็นประเภทอ้างอิง วัตถุ Cat ที่อยู่นอกนิพจน์แลมบ์ดาจะเปลี่ยนไป (จะได้รับชื่อและอายุที่ส่งผ่านภายใน) เวอร์ชันที่ซับซ้อนกว่าเล็กน้อยซึ่งใช้แลมบ์ดาที่คล้ายกัน:
public class Main {
public static void main(String[] args) {
Cat myCat = new Cat();
System.out.println(myCat);
Settable<Cat> s = (obj, name, age) -> {
obj.setName(name);
obj.setAge(age);
};
changeEntity(myCat, s);
System.out.println(myCat);
}
private static <T extends WithNameAndAge> void changeEntity(T entity, Settable<T> s) {
s.set(entity, "Murzik", 3);
}
}
interface WithNameAndAge {
void setName(String name);
void setAge(int age);
}
interface Settable<C extends WithNameAndAge> {
void set(C entity, String name, int age);
}
class Cat implements WithNameAndAge {
private String name;
private int age;
@Override
public void setName(String name) {
this.name = name;
}
@Override
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Cat{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
ผลลัพธ์: Cat{name='null', age=0} Cat{name='Murzik', age=3} อย่างที่คุณเห็น ในตอนแรกวัตถุ Cat มีสถานะเดียว แต่หลังจากใช้นิพจน์แลมบ์ดา สถานะก็เปลี่ยนไป . นิพจน์ Lambda ทำงานได้ดีกับยาชื่อสามัญ และหากเราต้องการสร้างคลาส
Dog
เช่น ที่จะนำไปใช้ด้วย
WithNameAndAge
ดังนั้นในวิธีที่
main()
เราสามารถดำเนินการเดียวกันกับ Dog ได้โดยไม่ต้องเปลี่ยนนิพจน์แลมบ์ดาเลย
ภารกิจที่ 3 เขียนอินเทอร์เฟซการทำงานด้วยวิธีการที่รับตัวเลขและส่งกลับค่าบูลีน เขียนการใช้งานอินเทอร์เฟซดังกล่าวในรูปแบบของนิพจน์แลมบ์ดาที่ส่งคืน
true
หากตัวเลขที่ส่งผ่านหารด้วย 13 ลงตัวโดยไม่มีเศษ ภารกิจ ที่
4 เขียนอินเทอร์เฟซการทำงานด้วยวิธีการที่รับสองสตริงและส่งกลับสตริงเดียวกัน เขียนการใช้งานอินเทอร์เฟซดังกล่าวในรูปแบบของแลมบ์ดาที่ส่งคืนสตริงที่ยาวที่สุด
ภารกิจที่ 5 เขียนอินเทอร์เฟซการทำงานด้วยวิธีการที่ยอมรับจำนวนเศษส่วนสามตัว:
a
,
b
และ
c
ส่งกลับจำนวนเศษส่วนเดียวกัน เขียนการใช้งานอินเทอร์เฟซดังกล่าวในรูปแบบของนิพจน์แลมบ์ดาที่ส่งคืนการแบ่งแยก ใครลืม
D = b^2 - 4ac .
ภารกิจที่ 6 ใช้อินเทอร์เฟซการทำงานจากภารกิจที่ 5 เขียนนิพจน์แลมบ์ดาที่ส่งคืนผลลัพธ์ของการดำเนิน
a * b^c
การ
เป็นที่นิยมเกี่ยวกับนิพจน์แลมบ์ดาใน Java พร้อมตัวอย่างและงาน ส่วนที่ 2
GO TO FULL VERSION