Comparator.reversed () ไม่รวบรวมโดยใช้ lambda


111

ฉันมีรายการที่มีวัตถุผู้ใช้บางอย่างและฉันกำลังพยายามจัดเรียงรายการ แต่ใช้งานได้โดยใช้การอ้างอิงวิธีการเท่านั้นด้วยนิพจน์แลมบ์ดาคอมไพเลอร์ให้ข้อผิดพลาด:

List<User> userList = Arrays.asList(u1, u2, u3);
userList.sort(Comparator.comparing(u -> u.getName())); // works
userList.sort(Comparator.comparing(User::getName).reversed()); // works
userList.sort(Comparator.comparing(u -> u.getName()).reversed()); // Compiler error

ข้อผิดพลาด:

com\java8\collectionapi\CollectionTest.java:35: error: cannot find symbol
            userList.sort(Comparator.comparing(u -> u.getName()).reversed());
                                                     ^
symbol:   method getName()
location: variable u of type Object
1 error

คำตอบ:


145

นี่เป็นจุดอ่อนในกลไกการอ้างอิงประเภทของคอมไพเลอร์ เพื่อที่จะสรุปประเภทของuในแลมบ์ดาที่ประเภทเป้าหมายสำหรับแลมบ์ดาจะต้องมีการจัดตั้ง สำเร็จดังต่อไปนี้ คาดว่าข้อโต้แย้งของพิมพ์userList.sort() Comparator<User>ในบรรทัดแรก, ความต้องการที่จะกลับมาComparator.comparing() Comparator<User>ซึ่งหมายความว่าComparator.comparing()จำเป็นต้องมีFunctionการUserโต้แย้ง ดังนั้นในแลมด้าในบรรทัดแรกuต้องเป็นประเภทUserและทุกอย่างทำงานได้

ในบรรทัดที่สองและสามการพิมพ์เป้าหมายจะหยุดชะงักเมื่อมีสายเรียกเข้า reversed()ในสายที่สองและสามพิมพ์เป้าหมายจะหยุดชะงักจากการปรากฏตัวของการโทรไปฉันไม่แน่ใจว่าทำไม ทั้งผู้รับและประเภทส่งคืนreversed()คือComparator<T>ดังนั้นจึงดูเหมือนว่าเป้าหมายชนิดควรจะแพร่กระจายกลับไปรับ แต่มันไม่ได้เป็น (อย่างที่ฉันพูดมันเป็นจุดอ่อน)

ในบรรทัดที่สองการอ้างอิงวิธีการให้ข้อมูลประเภทเพิ่มเติมที่เติมเต็มช่องว่างนี้ ข้อมูลนี้ไม่อยู่ในบรรทัดที่สามดังนั้นคอมไพเลอร์จึงสรุปได้uได้ว่าเป็นObject(ทางเลือกสุดท้ายของการอนุมาน) ซึ่งล้มเหลว

เห็นได้ชัดว่าหากคุณสามารถใช้การอ้างอิงวิธีการได้ให้ทำเช่นนั้นและจะได้ผล บางครั้งคุณไม่สามารถใช้การอ้างอิงเมธอดได้เช่นหากคุณต้องการส่งผ่านพารามิเตอร์เพิ่มเติมคุณจึงต้องใช้นิพจน์แลมบ์ดา ในกรณีนั้นคุณจะต้องระบุประเภทพารามิเตอร์ที่ชัดเจนในแลมบ์ดา:

userList.sort(Comparator.comparing((User u) -> u.getName()).reversed());

อาจเป็นไปได้ที่คอมไพเลอร์จะได้รับการปรับปรุงเพื่อให้ครอบคลุมกรณีนี้ในรีลีสในอนาคต


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

1
@StuartMarks คุณ "ไม่แน่ใจทั้งหมดว่าทำไม" คอมไพเลอร์ทำหน้าที่เช่นนี้ แต่สเปคภาษาบอกว่าอย่างไร? ควรมีข้อมูลที่เพียงพอในการกำหนดประเภททั่วไปตามข้อกำหนดภาษาหรือไม่ หากเป็นเช่นนั้นนี่เป็นบั๊กของคอมไพเลอร์และควรยื่นและจัดการตามนั้น มิฉะนั้นจะเป็นพื้นที่ที่ควรปรับปรุงภาษา Java มันคืออะไร?
Garret Wilson

8
ผมคิดว่าเราสามารถรักษาความคิดเห็นของไบรอันเป็นที่ชัดเจนขณะที่เขาเขียนข้อกำหนดในคำถาม :-)
minimalis

1
น่าเศร้าที่ไม่มีสิ่งนี้อธิบายได้ว่าเหตุใดจึงทำงานโดยไม่มีการย้อนกลับในขณะที่ไม่ทำงานกับการย้อนกลับ
Chris311

90

คุณสามารถหลีกเลี่ยงข้อ จำกัด นี้ได้โดยใช้สองอาร์กิวเมนต์Comparator.comparingโดยComparator.reverseOrder()เป็นอาร์กิวเมนต์ที่สอง:

users.sort(comparing(User::getName, reverseOrder()));

4
ดี. ฉันชอบสิ่งนี้ดีกว่าการใช้แลมด้าที่พิมพ์อย่างชัดเจน users.sort(reverseOrder(comparing(User::getName)));หรือดีกว่ายัง
โรล

10
โปรดทราบว่าreverseOrder(Comparator<T>)วิธีการดังกล่าวอยู่ในความไม่ได้อยู่ในjava.util.Collections Comparator
โรล
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.