โค้ดยิม/จาวาบล็อก/สุ่ม/คำอธิบายของแลมบ์ดานิพจน์ในภาษาจาวา พร้อมตัวอย่างและงาน. ส...
John Squirrels
ระดับ
San Francisco

คำอธิบายของแลมบ์ดานิพจน์ในภาษาจาวา พร้อมตัวอย่างและงาน. ส่วนที่ 1

เผยแพร่ในกลุ่ม
บทความนี้มีไว้เพื่อใคร?
  • สำหรับคนที่คิดว่ารู้จัก Java Core ดีอยู่แล้ว แต่ไม่มีเงื่อนงำเกี่ยวกับแลมบ์ดานิพจน์ใน Java หรือบางทีพวกเขาอาจเคยได้ยินบางอย่างเกี่ยวกับการแสดงออกของแลมบ์ดา แต่ขาดรายละเอียด
  • เหมาะสำหรับผู้ที่มีความเข้าใจเกี่ยวกับการแสดงออกของแลมบ์ดา แต่ยังรู้สึกหวาดกลัวและไม่คุ้นเคยกับการใช้พวกมัน
คำอธิบายของแลมบ์ดานิพจน์ในภาษาจาวา  พร้อมตัวอย่างและงาน.  ตอนที่ 1 - 1หากคุณไม่เหมาะกับหมวดหมู่ใดหมวดหมู่หนึ่งเหล่านี้ คุณอาจพบว่าบทความนี้น่าเบื่อ มีข้อบกพร่อง หรือโดยทั่วไปแล้วไม่ใช่ถ้วยชาของคุณ ในกรณีนี้ คุณสามารถข้ามไปยังสิ่งอื่นได้ หรือหากคุณเชี่ยวชาญในเรื่องนี้ โปรดแสดงความคิดเห็นในความคิดเห็นว่าฉันจะปรับปรุงหรือเสริมบทความได้อย่างไร เนื้อหาไม่ได้อ้างว่ามีคุณค่าทางวิชาการใด ๆ นับประสาอะไรกับความแปลกใหม่ ค่อนข้างตรงกันข้าม: ฉันจะพยายามอธิบายสิ่งต่าง ๆ ที่ซับซ้อน (สำหรับบางคน) ให้เรียบง่ายที่สุด คำขอให้อธิบาย Stream API เป็นแรงบันดาลใจให้ฉันเขียนสิ่งนี้ ฉันคิดเกี่ยวกับเรื่องนี้และตัดสินใจว่าตัวอย่างสตรีมบางส่วนของฉันอาจไม่สามารถเข้าใจได้หากไม่มีความเข้าใจในการแสดงออกของแลมบ์ดา ดังนั้นเราจะเริ่มด้วยการแสดงออกของแลมบ์ดา คุณต้องรู้อะไรบ้างเพื่อทำความเข้าใจบทความนี้
  1. คุณควรเข้าใจการเขียนโปรแกรมเชิงวัตถุ (OOP) กล่าวคือ:

    • คลาส วัตถุ และความแตกต่างระหว่างพวกมัน
    • อินเทอร์เฟซ ความแตกต่างระหว่างคลาส และความสัมพันธ์ระหว่างอินเทอร์เฟซและคลาส
    • เมธอด, วิธีเรียกใช้เมธอดนามธรรม (เช่นเมธอดที่ไม่มีการนำไปใช้), พารามิเตอร์เมธอด, อาร์กิวเมนต์เมธอดและวิธีส่งต่อ
    • ตัวแก้ไขการเข้าถึง วิธีการ/ตัวแปรแบบสแตติก วิธีการ/ตัวแปรสุดท้าย
    • การสืบทอดคลาสและอินเตอร์เฟส การสืบทอดหลายอินเตอร์เฟส
  2. ความรู้เกี่ยวกับ Java Core: ประเภททั่วไป (ทั่วไป), คอลเลกชัน (รายการ), เธรด
มาเริ่มกันเลย

ประวัติเล็กน้อย

การแสดงออกของแลมบ์ดามาถึง Java จากการเขียนโปรแกรมเชิงฟังก์ชัน และมาจากคณิตศาสตร์ ในสหรัฐอเมริกาช่วงกลางศตวรรษที่ 20 Alonzo Church ซึ่งชื่นชอบคณิตศาสตร์และนามธรรมทุกประเภททำงานที่มหาวิทยาลัยพรินซ์ตัน Alonzo Church เป็นผู้คิดค้นแคลคูลัสแลมบ์ดา ซึ่งเดิมเป็นชุดของแนวคิดนามธรรมที่ไม่เกี่ยวข้องกับการเขียนโปรแกรมโดยสิ้นเชิง นักคณิตศาสตร์เช่น Alan Turing และ John von Neumann ทำงานที่มหาวิทยาลัย Princeton ในเวลาเดียวกัน ทุกอย่างมารวมกัน: คริสตจักรมาพร้อมกับแคลคูลัสแลมบ์ดา ทัวริงได้พัฒนาเครื่องคำนวณเชิงนามธรรมของเขา ซึ่งปัจจุบันรู้จักกันในชื่อ "เครื่องทัวริง" และฟอน นอยมันน์เสนอสถาปัตยกรรมคอมพิวเตอร์ที่เป็นพื้นฐานของคอมพิวเตอร์สมัยใหม่ (ปัจจุบันเรียกว่า "สถาปัตยกรรมฟอน นอยมันน์") ขณะนั้น อลองโซเชิร์ช' ความคิดของเขาไม่เป็นที่รู้จักดีเท่าผลงานของเพื่อนร่วมงานของเขา (ยกเว้นสาขาคณิตศาสตร์บริสุทธิ์) อย่างไรก็ตาม หลังจากนั้นไม่นาน จอห์น แมคคาร์ธี (ซึ่งจบการศึกษาจากมหาวิทยาลัยพรินซ์ตันเช่นกัน และในช่วงเวลาของเรื่องราวของเรา เป็นพนักงานของสถาบันเทคโนโลยีแมสซาชูเซตส์) เริ่มสนใจแนวคิดของศาสนจักร ในปี พ.ศ. 2501 เขาได้สร้างภาษาโปรแกรมเชิงฟังก์ชันภาษาแรก LISP ตามแนวคิดเหล่านั้น และ 58 ปีต่อมา แนวคิดของการเขียนโปรแกรมเชิงฟังก์ชันได้รั่วไหลไปยัง Java 8 ผ่านไปไม่ถึง 70 ปี... พูดตามตรง นี่ไม่ใช่ระยะเวลาที่นานที่สุดสำหรับแนวคิดทางคณิตศาสตร์ที่จะนำไปใช้ในทางปฏิบัติ พนักงานของสถาบันเทคโนโลยีแมสซาชูเซตส์) เริ่มสนใจแนวคิดของศาสนจักร ในปี พ.ศ. 2501 เขาได้สร้างภาษาโปรแกรมเชิงฟังก์ชันภาษาแรก LISP ตามแนวคิดเหล่านั้น และ 58 ปีต่อมา แนวคิดของการเขียนโปรแกรมเชิงฟังก์ชันได้รั่วไหลไปยัง Java 8 ผ่านไปไม่ถึง 70 ปี... พูดตามตรง นี่ไม่ใช่ระยะเวลาที่นานที่สุดสำหรับแนวคิดทางคณิตศาสตร์ที่จะนำไปใช้ในทางปฏิบัติ พนักงานของสถาบันเทคโนโลยีแมสซาชูเซตส์) เริ่มสนใจแนวคิดของศาสนจักร ในปี พ.ศ. 2501 เขาได้สร้างภาษาโปรแกรมเชิงฟังก์ชันภาษาแรก LISP ตามแนวคิดเหล่านั้น และ 58 ปีต่อมา แนวคิดของการเขียนโปรแกรมเชิงฟังก์ชันได้รั่วไหลไปยัง Java 8 ผ่านไปไม่ถึง 70 ปี... พูดตามตรง นี่ไม่ใช่ระยะเวลาที่นานที่สุดสำหรับแนวคิดทางคณิตศาสตร์ที่จะนำไปใช้ในทางปฏิบัติ

หัวใจของเรื่อง

การแสดงออกของแลมบ์ดาเป็นฟังก์ชันชนิดหนึ่ง คุณสามารถพิจารณาได้ว่าเป็นวิธี Java ธรรมดา แต่มีความสามารถพิเศษในการส่งผ่านไปยังวิธีอื่นเป็นอาร์กิวเมนต์ ถูกตัอง. มันเป็นไปได้ที่จะส่งไม่เพียงแค่ตัวเลข สตริง และแมวไปยังเมธอดเท่านั้น แต่ยังรวมถึงเมธอดอื่นๆ ด้วย! เราต้องการสิ่งนี้เมื่อใด มันจะมีประโยชน์ เช่น ถ้าเราต้องการส่งวิธีการโทรกลับ นั่นคือหากเราต้องการให้เมธอดที่เราเรียกมีความสามารถในการเรียกเมธอดอื่นที่เราส่งไปให้ กล่าวอีกนัยหนึ่ง ดังนั้นเราจึงมีความสามารถในการส่งการโทรกลับหนึ่งครั้งภายใต้สถานการณ์บางอย่าง และการส่งกลับที่แตกต่างกันในกรณีอื่นๆ และเพื่อให้เมธอดของเราที่รับการโทรกลับโทรหาพวกเขา การเรียงลำดับเป็นตัวอย่างง่ายๆ สมมติว่าเรากำลังเขียนอัลกอริธึมการเรียงลำดับที่ชาญฉลาดที่มีลักษณะดังนี้:
public void mySuperSort() {
    // We do something here
    if(compare(obj1, obj2) > 0)
    // And then we do something here
}
ในifคำสั่ง เราเรียกcompare()เมธอด โดยส่งผ่านวัตถุสองชิ้นเพื่อเปรียบเทียบ และเราต้องการทราบว่าวัตถุใดในเหล่านี้ "มากกว่า" เราถือว่าสิ่งที่ "มากกว่า" มาก่อนสิ่งที่ "น้อยกว่า" ฉันใส่ "มากกว่า" ในเครื่องหมายคำพูด เพราะเรากำลังเขียนวิธีสากลที่จะรู้วิธีจัดเรียง ไม่เพียงแต่เรียงจากน้อยไปมาก แต่ยังเรียงจากมากไปน้อยด้วย (ในกรณีนี้ วัตถุที่ "มากกว่า" จะเป็นวัตถุที่ "น้อยกว่า" , และในทางกลับกัน). ในการตั้งค่าอัลกอริทึมเฉพาะสำหรับการจัดเรียงของเรา เราจำเป็นต้องมีกลไกบางอย่างเพื่อส่งผ่านไปยังmySuperSort()วิธีการ ของเรา ด้วยวิธีนี้เราจะสามารถ "ควบคุม" วิธีการของเราเมื่อมีการเรียกใช้ แน่นอน เราสามารถเขียนสองวิธีแยกกัน — mySuperSortAscend()และmySuperSortDescend()— สำหรับเรียงลำดับจากน้อยไปหามาก หรือเราสามารถส่งผ่านอาร์กิวเมนต์บางอย่างไปยังเมธอด (เช่น ตัวแปรบูลีน ถ้าจริง ให้เรียงลำดับจากน้อยไปมาก และถ้าเป็นเท็จ ให้เรียงลำดับจากมากไปน้อย) แต่ถ้าเราต้องการเรียงลำดับบางสิ่งที่ซับซ้อน เช่น รายการสตริงอาร์เรย์ วิธีการ ของเราจะmySuperSort()รู้วิธีจัดเรียงอาร์เรย์สตริงเหล่านี้ได้ อย่างไร ตามขนาด? ตามความยาวรวมของคำทั้งหมด? อาจเรียงตามตัวอักษรตามสตริงแรกในอาร์เรย์ แล้วถ้าเราต้องการจัดเรียงรายการอาร์เรย์ตามขนาดอาร์เรย์ในบางกรณี และตามความยาวสะสมของคำทั้งหมดในแต่ละอาร์เรย์ในกรณีอื่นๆ ล่ะ ฉันคาดว่าคุณคงเคยได้ยินเกี่ยวกับตัวเปรียบเทียบแล้ว และในกรณีนี้ เราจะส่งต่อไปยังวิธีการจัดเรียงของเรา ซึ่งเป็นวัตถุตัวเปรียบเทียบที่อธิบายอัลกอริทึมการเรียงลำดับที่ต้องการ เพราะมีมาตรฐานsort()วิธีการดำเนินการตามหลักการเดียวกับmySuperSort()ฉันจะใช้sort()ในตัวอย่างของฉัน
String[] array1 = {"Dota", "GTA5", "Halo"};
String[] array2 = {"I", "really", "love", "Java"};
String[] array3 = {"if", "then", "else"};

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[]> sortByCumulativeWordLength = 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);
ผลลัพธ์:
Dota GTA5 Halo
if then else
I really love Java
อาร์เรย์จะจัดเรียงตามจำนวนคำในแต่ละอาร์เรย์ อาร์เรย์ที่มีคำน้อยกว่าถือเป็น "น้อยกว่า" นั่นเป็นเหตุผลที่มันมาก่อน อาร์เรย์ที่มีคำมากกว่าจะถือว่า "มากกว่า" และวางไว้ที่ส่วนท้าย หากเราส่งตัวเปรียบเทียบอื่นไปยังsort()เมธอด เช่นsortByCumulativeWordLengthเราจะได้ผลลัพธ์ที่แตกต่างกัน:
if then else
Dota GTA5 Halo
I really love Java
ตอนนี้ are arrays จัดเรียงตามจำนวนตัวอักษรทั้งหมดในคำของอาร์เรย์ ในอาร์เรย์แรกมีตัวอักษร 10 ตัว ตัวที่สองมี 12 ตัว และตัวที่สามมี 15 ตัว หากเรามีตัวเปรียบเทียบเพียงตัวเดียว เราก็ไม่ต้องประกาศตัวแปรแยกต่างหากสำหรับตัวเปรียบเทียบ แต่เราสามารถสร้างคลาสที่ไม่ระบุตัวตนในเวลาที่เรียกใช้เมธอดsort()แทน สิ่งนี้:
String[] array1 = {"Dota", "GTA5", "Halo"};
String[] array2 = {"I", "really", "love", "Java"};
String[] array3 = {"if", "then", "else"};

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;
    }
});
ที่นี่เราใช้รายการอาร์เรย์ของเราและเรียกsort()เมธอดของมัน ซึ่งเราส่งออบเจกต์ตัวเปรียบเทียบด้วยcompare()เมธอดเดียว (ชื่อนั้นไม่สำคัญสำหรับเรา ท้ายที่สุด มันเป็นเมธอดเดียวของออบเจกต์นี้ ดังนั้นเราจึงไม่ผิดพลาด) วิธีนี้มีพารามิเตอร์สองตัวที่เราจะใช้ร่วมกัน หากคุณกำลังทำงานใน IntelliJ IDEA คุณอาจเห็นว่ามีการเสนอให้ย่อโค้ดอย่างมีนัยสำคัญดังนี้:
arrays.sort((o1, o2) -> o1.length - o2.length);
สิ่งนี้จะลดหกบรรทัดเป็นบรรทัดสั้นเดียว 6 บรรทัดถูกเขียนใหม่เป็นหนึ่งสั้น มีบางอย่างหายไป แต่ฉันรับประกันได้ว่ามันไม่ได้สำคัญอะไร รหัสนี้จะทำงานแบบเดียวกับที่ทำกับคลาสนิรนาม ภารกิจที่ 2ลองเดาในการเขียนคำตอบใหม่ให้กับภารกิจที่ 1 โดยใช้นิพจน์แลมบ์ดา (อย่างน้อยที่สุด ขอให้ IntelliJ IDEA แปลงคลาสนิรนามของคุณเป็นนิพจน์แลมบ์ดา)

พูดคุยเกี่ยวกับอินเทอร์เฟซ

โดยหลักการแล้ว อินเทอร์เฟซเป็นเพียงรายการของวิธีการเชิงนามธรรม เมื่อเราสร้างคลาสที่ใช้อินเตอร์เฟสบางตัว คลาสของเราต้องอิมพลีเมนต์เมธอดที่รวมอยู่ในอินเทอร์เฟซ (หรือเราต้องทำให้คลาสนามธรรม) มีอินเตอร์เฟสที่มีเมธอดต่างๆ มากมาย (เช่น  List) และมีอินเตอร์เฟสที่มีเมธอดเดียว (เช่นComparatorหรือRunnable) มีอินเทอร์เฟซที่ไม่มีเมธอดเดียว (เรียกว่าอินเทอร์เฟซสำหรับทำเครื่องหมาย เช่นSerializable) ส่วนต่อประสานที่มีเพียงวิธีเดียวเรียกว่าส่วนต่อประสานการทำงาน ใน Java 8 พวกเขาจะถูกทำเครื่องหมายด้วยคำอธิบายประกอบพิเศษ:@FunctionalInterface. เป็นอินเทอร์เฟซแบบวิธีเดียวที่เหมาะสำหรับเป็นประเภทเป้าหมายสำหรับนิพจน์แลมบ์ดา ดังที่ฉันได้กล่าวไว้ข้างต้น การแสดงออกของแลมบ์ดาเป็นวิธีการที่ห่อหุ้มวัตถุ และเมื่อเราส่งผ่านวัตถุดังกล่าว เรากำลังส่งผ่านวิธีการเดี่ยวนี้โดยพื้นฐานแล้ว กลายเป็นว่าเราไม่สนใจว่าเมธอดนั้นเรียกว่าอะไร สิ่งเดียวที่สำคัญสำหรับเราคือพารามิเตอร์ของเมธอด และแน่นอน เนื้อหาของเมธอด โดยพื้นฐานแล้ว การแสดงออกของแลมบ์ดาคือการปรับใช้ส่วนต่อประสานการทำงาน เมื่อใดก็ตามที่เราเห็นอินเทอร์เฟซที่มีเมธอดเดียว คลาสนิรนามสามารถเขียนใหม่เป็นแลมบ์ดาได้ หากอินเทอร์เฟซมีเมธอดมากกว่าหรือน้อยกว่าหนึ่งเมธอด นิพจน์แลมบ์ดาจะไม่ทำงาน และเราจะใช้คลาสนิรนามหรือแม้แต่อินสแตนซ์ของคลาสธรรมดาแทน ตอนนี้ถึงเวลาที่จะเจาะลึกเข้าไปใน lambdas สักหน่อย :)

ไวยากรณ์

ไวยากรณ์ทั่วไปเป็นดังนี้:
(parameters) -> {method body}
นั่นคือ วงเล็บล้อมรอบพารามิเตอร์เมธอด "ลูกศร" (สร้างด้วยยัติภังค์และเครื่องหมายมากกว่า) และเมธอดในวงเล็บปีกกาเช่นเคย พารามิเตอร์สอดคล้องกับที่ระบุในวิธีการเชื่อมต่อ หากคอมไพเลอร์กำหนดประเภทตัวแปรได้อย่างชัดเจน (ในกรณีของเรา คอมไพลเลอร์รู้ว่าเรากำลังทำงานกับอาร์เรย์สตริง เนื่องจากListออบเจกต์ของเราพิมพ์โดยใช้String[]) คุณก็ไม่จำเป็นต้องระบุประเภทของตัวแปร
หากไม่ชัดเจนให้ระบุประเภท IDEA จะใช้สีเทาหากไม่จำเป็น
คุณสามารถอ่านเพิ่มเติมได้ในบทช่วยสอน Oracle นี้ และที่อื่นๆ สิ่งนี้เรียกว่า " การพิมพ์เป้าหมาย " คุณสามารถตั้งชื่อตัวแปรอะไรก็ได้ที่คุณต้องการ — คุณไม่จำเป็นต้องใช้ชื่อเดียวกันกับที่ระบุในอินเทอร์เฟซ หากไม่มีพารามิเตอร์ ให้ระบุวงเล็บว่าง หากมีเพียงพารามิเตอร์เดียว ให้ระบุชื่อตัวแปรโดยไม่ต้องใส่วงเล็บ ตอนนี้เราเข้าใจพารามิเตอร์แล้ว ก็ถึงเวลาหารือเกี่ยวกับเนื้อหาของนิพจน์แลมบ์ดา ภายในวงเล็บปีกกา คุณเขียนโค้ดเหมือนกับวิธีการทั่วไป หากรหัสของคุณประกอบด้วยบรรทัดเดียว คุณสามารถละเว้นวงเล็บปีกกาทั้งหมดได้ (คล้ายกับคำสั่ง if และ for-loop) หากแลมบ์ดาบรรทัดเดียวส่งคืนบางสิ่ง คุณไม่จำเป็นต้องใส่ areturnคำแถลง. แต่ถ้าคุณใช้วงเล็บปีกกา คุณต้องระบุ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คือ ดังนั้นโค้ดนี้จะสร้างวัตถุด้วยวิธีการที่ไม่รับหรือส่งคืนสิ่งใด สิ่งนี้ตรงกับวิธีการRunnableของอินเตอร์เฟส อย่างสมบูรณ์แบบ run()นั่นเป็นเหตุผลที่เราสามารถใส่นิพจน์แลมบ์ดานี้ใน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) -> x
และนี่คือตัวอย่างที่มีคำสั่ง return ที่ชัดเจน:
x -> {
    return x;
}
หรือแบบนี้กับวงเล็บและ return คำสั่ง:
(x) -> {
    return x;
}
หรือมีการระบุประเภทอย่างชัดเจน (และด้วยวงเล็บ):
(int x) -> x
ตัวอย่างที่ 7
x -> ++x
เรารับxและส่งคืน แต่หลังจากเพิ่ม 1 แล้ว คุณสามารถเขียนแลมบ์ดาใหม่ได้ดังนี้:
x -> x + 1
ในทั้งสองกรณี เราไม่ใส่วงเล็บรอบพารามิเตอร์และเนื้อหาของเมธอดพร้อมกับคำreturnสั่ง เนื่องจากเป็นทางเลือก เวอร์ชันที่มีวงเล็บและคำสั่ง return มีอยู่ในตัวอย่างที่ 6 ตัวอย่างที่ 8
(x, y) -> 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วัตถุStringชื่อ และอายุ int ในเมธอดเอง เราใช้ชื่อและอายุที่ส่งผ่านเพื่อตั้งค่าตัวแปรบนแมว เนื่องจากcatวัตถุของเราเป็นประเภทการอ้างอิง มันจะถูกเปลี่ยนนอกนิพจน์แลมบ์ดา (มันจะได้รับชื่อและอายุที่ส่งผ่าน) ต่อไปนี้เป็นเวอร์ชันที่ซับซ้อนขึ้นเล็กน้อยซึ่งใช้แลมบ์ดาที่คล้ายกัน:
public class Main {

    public static void main(String[] args) {
        // Create a cat and display it to confirm that it is "empty"
        Cat myCat = new Cat();
        System.out.println(myCat);

        // Create a lambda
        Settable<Cat> s = (obj, name, age) -> {
            obj.setName(name);
            obj.setAge(age);

        };

        // Call a method to which we pass the cat and lambda
        changeEntity(myCat, s);

        // Display the cat on the screen and see that its state has changed (it has a name and age)
        System.out.println(myCat);

    }

    private static <T extends HasNameAndAge>  void changeEntity(T entity, Settable<T> s) {
        s.set(entity, "Smokey", 3);
    }
}

interface HasNameAndAge {
    void setName(String name);
    void setAge(int age);
}

interface Settable<C extends HasNameAndAge> {
    void set(C entity, String name, int age);
}

class Cat implements HasNameAndAge {
    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='Smokey', age=3}
อย่างที่คุณเห็นCatวัตถุมีสถานะเดียว จากนั้นสถานะก็เปลี่ยนไปหลังจากที่เราใช้นิพจน์แลมบ์ดา การแสดงออกของแลมบ์ดาผสมผสานอย่างลงตัวกับยาสามัญ และถ้าเราต้องการสร้างDogคลาสที่นำไปใช้ด้วยHasNameAndAgeเราก็สามารถดำเนินการเดียวกันในDogเมธอดmain() ได้โดยไม่ต้องเปลี่ยนนิพจน์แลมบ์ดา ภารกิจที่ 3เขียนส่วนต่อประสานการทำงานด้วยวิธีการที่รับตัวเลขและส่งกลับค่าบูลีน เขียนการใช้งานอินเทอร์เฟซดังกล่าวเป็นนิพจน์แลมบ์ดาที่คืนค่าจริงหากตัวเลขที่ส่งผ่านหารด้วย 13 ลงตัว ภารกิจที่ 4เขียนส่วนต่อประสานการทำงานด้วยเมธอดที่รับสองสตริงและส่งคืนสตริงด้วย เขียนการใช้งานอินเทอร์เฟซดังกล่าวเป็นนิพจน์แลมบ์ดาที่ส่งคืนสตริงที่ยาวกว่า ภารกิจที่ 5.เขียนอินเทอร์เฟซการทำงานด้วยเมธอดที่ใช้เลขทศนิยมสามตัว: a, b และ c และส่งคืนเลขทศนิยมด้วย เขียนการใช้งานอินเทอร์เฟซดังกล่าวเป็นนิพจน์แลมบ์ดาที่ส่งคืนค่าการเลือกปฏิบัติ ในกรณีที่คุณลืม นั่นD = b^2 — 4acคือ งาน 6.ใช้อินเทอร์เฟซการทำงานจากงาน 5 เขียนนิพจน์แลมบ์ดาที่ส่งคืนผลลัพธ์a * b^cของ คำอธิบายของแลมบ์ดานิพจน์ในภาษาจาวา พร้อมตัวอย่างและงาน. ส่วนที่ 2
ความคิดเห็น
  • เป็นที่นิยม
  • ใหม่
  • เก่า
คุณต้องลงชื่อเข้าใช้เพื่อแสดงความคิดเห็น
หน้านี้ยังไม่มีความคิดเห็นใด ๆ