นิพจน์แลมบ์ดาเทียบกับการอ้างอิงเมธอด [ปิด]


114

IntelliJ ยังคงเสนอให้ฉันแทนที่นิพจน์แลมบ์ดาด้วยการอ้างอิงวิธีการ

มีวัตถุประสงค์แตกต่างกันระหว่างทั้งสองอย่างหรือไม่?


4
ไม่จริงฉันไม่เห็นวัตถุใด ๆ ! ดูเหมือนเป็นการโทรหาฉันแบบคงที่
เจอราร์ด

9
แน่นอน! แต่ "ไม่เจอ" เหมือนกัน ... มันเป็นเรื่องของรสนิยมฉันกังวลเกี่ยวกับแง่มุมทางเทคนิคมากกว่า อันที่จริงอย่างที่คุณบอกไปแล้วว่าพวกเขาเหมือนกันนั่นเป็นคำตอบที่ดีสำหรับฉัน อย่างไรก็ตามตามที่ IntelliJ เสนอฉันเดาว่าโดยทั่วไปแล้วการดูการอ้างอิงวิธีการมากกว่า lambda (ไม่ใช่สำหรับฉัน)
เจอราร์ด

15
โค้ดของนิพจน์แลมบ์ดาถูกคอมไพล์ด้วยวิธีการสังเคราะห์ในขณะที่การอ้างอิงเมธอดทำงานโดยไม่มี (ข้อยกเว้น: โครงสร้างพิเศษเช่นType[]::new) คลาสที่ไม่ระบุชื่อที่สร้างขึ้นในรันไทม์จะเหมือนกัน JRE ไม่ได้สร้างความแตกต่างระหว่างพวกเขา ดังนั้นการใช้การอ้างอิงวิธีการจะช่วยให้คุณประหยัดวิธีการหนึ่งในโค้ดที่คอมไพล์ของคุณในทางกลับกันคุณไม่สามารถหยุดได้เมื่อทำการดีบักทีละขั้นตอน ...
Holger

6
ตอนนี้คำถามกำลังจะถูกปิดลง ... น่าเสียดายสำหรับผู้ใช้ทุกคนเช่นฉันที่ไม่รู้ความแตกต่างระหว่างแลมบ์ดาและการอ้างอิงแม้ว่าจะไม่มีข้อชี้ขาดก็ตาม ฉันก็สงสัยเหมือนกันว่าทำไมไม่มีใครกล้าตอบ? นั่นคือคำตอบที่ใช่สำหรับฉัน
เจอราร์ด

3
การตอบคำถามแบบปิดอายุสองปีอาจเป็นความคิดที่ไม่ดี แต่สำหรับผู้ที่อ่านคำถามจริง ๆ นี่คือสิ่งที่บทช่วยสอนของ Oracle ( docs.oracle.com/javase/tutorial/java/javaOO/… ) กล่าวว่า: การอ้างอิงวิธี ... คือ นิพจน์แลมบ์ดาขนาดกะทัดรัดอ่านง่ายสำหรับเมธอดที่มีชื่ออยู่แล้ว
พรุ่งนี้

คำตอบ:


187

ให้ฉันเสนอมุมมองเกี่ยวกับสาเหตุที่เราเพิ่มคุณลักษณะนี้ลงในภาษาเมื่อเห็นได้ชัดว่าเราไม่จำเป็นต้องทำอย่างเคร่งครัด (วิธีการอ้างอิงทั้งหมดสามารถแสดงเป็น lambdas ได้)

ทราบว่ามีคำตอบไม่เหมาะสม ใครก็ตามที่ระบุว่า "ใช้ method ref แทน lambda เสมอ" หรือ "always use a lambda แทน method ref" ควรละเว้น

คำถามนี้มีความคล้ายคลึงกับ "เมื่อใดที่ฉันควรใช้คลาสที่มีชื่อเทียบกับคลาสที่ไม่ระบุตัวตน" และคำตอบก็เหมือนกันเมื่อคุณพบว่ามันอ่านง่ายขึ้น มีหลายกรณีที่แน่นอนอย่างใดอย่างหนึ่ง แต่มีโฮสต์ของสีเทาอยู่ตรงกลางและต้องใช้วิจารณญาณ

ทฤษฎีที่อยู่เบื้องหลัง refs วิธีการง่ายๆคือ: ชื่อเรื่อง หากเมธอดมีชื่อให้อ้างถึงด้วยชื่อแทนที่จะใช้ถุงรหัสที่จำเป็นซึ่งในที่สุดก็หมุนไปรอบ ๆ และเรียกใช้มันมักจะชัดเจนและอ่านง่ายกว่า (แต่ไม่เสมอไป!)

ข้อโต้แย้งเกี่ยวกับประสิทธิภาพหรือเกี่ยวกับการนับตัวอักษรส่วนใหญ่เป็นปลาชนิดหนึ่งสีแดงและคุณควรเพิกเฉยต่อสิ่งเหล่านี้ เป้าหมายคือการเขียนโค้ดที่ชัดเจนว่ามันทำอะไร บ่อยมาก (แต่ไม่เสมอไป!) วิธีการอ้างอิงจะชนะในเมตริกนี้ดังนั้นเราจึงรวมไว้เป็นตัวเลือกเพื่อใช้ในกรณีเหล่านั้น

การพิจารณาที่สำคัญเกี่ยวกับว่าวิธีการที่อ้างถึงชี้แจงหรือเจตนาทำให้สับสนคือเห็นได้ชัดจากบริบทว่ารูปร่างของฟังก์ชันที่แสดงเป็นอย่างไร ในบางกรณี (เช่นmap(Person::getLastName)มันค่อนข้างชัดเจนจากบริบทที่จำเป็นต้องมีฟังก์ชั่นที่จับคู่สิ่งหนึ่งกับอีกสิ่งหนึ่งและในกรณีเช่นนี้การอ้างอิงเมธอดจะเปล่งประกายในกรณีอื่น ๆ การใช้ method ref ต้องการให้ผู้อ่านสงสัยว่าประเภทใด กำลังอธิบายถึงฟังก์ชันนี่เป็นสัญญาณเตือนว่าแลมด้าอาจอ่านได้ง่ายขึ้นแม้ว่าจะยาวกว่าก็ตาม

ในที่สุดสิ่งที่เราพบก็คือในตอนแรกคนส่วนใหญ่มักหลีกเลี่ยง method refs เพราะพวกเขารู้สึกว่าใหม่กว่าและแปลกกว่า lambdas และเริ่มพบว่าพวกเขา "อ่านได้น้อยกว่า" แต่เมื่อเวลาผ่านไปเมื่อพวกเขาคุ้นเคยกับไวยากรณ์ โดยทั่วไปจะเปลี่ยนพฤติกรรมและโน้มน้าวไปสู่การอ้างอิงวิธีการเมื่อทำได้ ดังนั้นโปรดทราบว่าปฏิกิริยาที่ "อ่านได้น้อยกว่า" ที่เป็นอัตวิสัยของคุณเองเกือบจะแน่นอนว่ามีความลำเอียงในเรื่องครอบครัวและคุณควรให้โอกาสตัวเองในการทำความคุ้นเคยกับทั้งสองอย่างก่อนที่จะแสดงความคิดเห็นเกี่ยวกับโวหาร


25
@Gerard ขอบคุณคำตอบที่ดีกว่านี้
Yassin Hajaj

3
@Gerard ดูเหมือนว่า Brian จะพูดว่า "No, there"
dj18

Brian ฉันได้ผลลัพธ์ที่แตกต่างกันโดยใช้การอ้างอิงวิธีการและแลมด้าสำหรับวิธีการเดียวกัน ไม่ควรใช้แทนกันได้หรือไม่?
Michel Feinstein

@mFeinstein สำหรับทุกวิธีการอ้างอิงจะมีแลมด้าที่เทียบเท่ากัน (ซึ่งคุณอาจใช้หรือไม่ใช้ก็ได้) โพสต์รหัสเป็นคำถาม?
Brian Goetz

ฉันต้องการ แต่ฉันกลัวรหัสอาจจะมีขนาดใหญ่เกินไปเพราะมันเป็น Android LiveData, ภายในFragmentที่ผมแปลงเป็นEventซึ่งจะถูกเรียกโดยViewModel... และพฤติกรรมที่แตกต่างที่เกิดขึ้นเมื่อ Android กลับไปเหมือนกันFragment.. ดังนั้นฉันกำลังมีช่วงเวลาที่ยากลำบากในการทำให้คำถามง่ายขึ้น
Michel Feinstein

10

นิพจน์แลมบ์ดาแบบยาวซึ่งประกอบด้วยข้อความจำนวนมากอาจลดความสามารถในการอ่านโค้ดของคุณ ในกรณีเช่นนี้การแยกข้อความเหล่านั้นด้วยวิธีการและอ้างอิงอาจเป็นทางเลือกที่ดีกว่า

สาเหตุอื่น ๆ อาจเป็นเรื่องของการใช้งานซ้ำ แทนที่จะคัดลอกและวางนิพจน์แลมบ์ดาของคำสั่งเพียงไม่กี่คำคุณสามารถสร้างวิธีการและเรียกใช้จากที่ต่างๆของโค้ดของคุณ


3
เปรียบเทียบ: houses.map(House::getName)และhouses.map(h -> h.getName()). แลมบ์ดาใช้อักขระน้อยกว่าสองตัว เป็นเรื่องจริงที่ประเภทนั้นไม่ชัดเจน แต่ IDE ใด ๆ จะบอกคุณได้และนอกจากนี้ควรใช้ lambdas เมื่อประเภทชัดเจน ฉันอาจเห็นด้วยกับคุณเกี่ยวกับการนำกลับมาใช้ใหม่ แต่ lambdas มีขนาดเล็กมากดังนั้นจึงสามารถถูกล่ามโซ่แทนที่จะสร้างวิธีการเฉพาะขนาดใหญ่ ในแง่นั้นวิธีการเล็ก ๆ สามารถใช้ซ้ำได้มากกว่าวิธีการที่ใหญ่และซับซ้อนและเนื่องจากความชัดเจนของ lambdas (และในระดับหนึ่งความฟุ่มเฟื่อย) จึงยังอ่านง่าย
เจอราร์ด

11
ฉันอยู่กับเจอรัลด์ ความสามารถในการอ่านได้รับผลจริงเมื่อคุณย้ายโค้ดออกดังนั้นคุณต้องข้ามไปที่โค้ดเพื่ออ่านต่อจากนั้นข้ามกลับ คุณต้องการให้รหัสที่เกี่ยวข้องทั้งหมดอยู่ในที่เดียวกัน นอกจากนี้ยังHouseเป็นตัวอย่างที่อ่อนโยนมาก ThreeStoryRedBrickHouseWithBlueDoorsสิ่งที่เกี่ยวกับ ฉันชอบการอ้างอิงวิธีการสำหรับ lambdas หลายอาร์กิวเมนต์และบางครั้งก็เน้นว่าแลมบ์ดาเป็นเพียงการเรียกใช้เมธอดเดียว มีข้อผิดพลาดน้อยกว่าที่จะผิดพลาดกับการอ้างอิงวิธีการ: คุณอาจสะกดผิดอาร์กิวเมนต์ที่ไซต์การใช้งานโดยบังเอิญอ้างถึงตัวแปรจากขอบเขตภายนอกเป็นต้น
Marko Topolnik

@ เจอราร์ดฉันไม่เห็นด้วยกับคำพูดแรกของคุณ หากคุณใช้การอธิบายชื่อเมธอดได้ดีและหากคุณแยกโค้ดกองใหญ่การย้ายโค้ดจะช่วยเพิ่มความสามารถในการอ่าน หากคุณใช้ชื่อวิธีการที่ทำให้สับสนฉันเห็นด้วยกับคุณ
Torsten
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.