เหตุใด. .compareTo () ในอินเทอร์เฟซในขณะที่. equals () อยู่ในคลาสใน Java


30

ฉันต้องการทราบว่าทำไมถึง.compareTo()อยู่ในComparableอินเทอร์เฟซในขณะที่วิธีการเช่น.equalsนั้นอยู่ในObjectชั้นเรียน สำหรับฉันดูเหมือนว่า.compareTo()จะไม่มีวิธีการเช่นนั้นอยู่ในObjectชั้นเรียนอยู่แล้ว

ที่จะใช้.compareTo()คุณใช้Comparableอินเตอร์เฟซและใช้.compareTo()วิธีการเพื่อวัตถุประสงค์ของคุณ สำหรับทาง.equals()วิธีการคุณเพียงแค่แทนที่วิธีการในชั้นเรียนของคุณเนื่องจากชั้นเรียนทั้งหมดได้รับมรดกจากObjectชั้นเรียน

คำถามของฉันคือเหตุใดจึงมีวิธีการ.compareTo()ในอินเทอร์เฟซที่คุณใช้งานมากกว่าในคลาสอย่างObject? เช่นเดียวกันทำไมจึงเป็น.equals()วิธีการในชั้นเรียนObjectและไม่ได้อยู่ในอินเตอร์เฟซบางอย่างที่จะดำเนินการ?


4
มีความเป็นไปได้ที่ซ้ำซ้อนของJava: ทำไมถึงมีอินเทอร์เฟซตัวเปรียบเทียบ
Andres F.

2
มันเป็นตัวเลือกการออกแบบของภาษาจาวา (ไม่ได้แปลว่ามันเป็นตัวเลือกที่ถูกต้อง) ในภาษาอื่น ๆ เช่นHaskellคุณต้องใช้อินเตอร์เฟซเท่าเทียมกันที่จะได้รับค่าเท่าเทียมกัน (ที่จริงคุณให้เป็นตัวอย่างกับEqtypeclass)
mucaho

2
คำถามเมตาที่เกี่ยวข้อง: คำถามเหล่านี้ซ้ำซ้อนกันหรือไม่

คำตอบ:


58

ไม่สามารถเปรียบเทียบวัตถุทั้งหมดได้ แต่วัตถุทั้งหมดสามารถตรวจสอบความเท่าเทียมกันได้ หากไม่มีสิ่งใดอีกใครสามารถดูว่ามีวัตถุสองรายการอยู่ที่ตำแหน่งเดียวกันในหน่วยความจำ (ความเท่าเทียมกันอ้างอิง)

วัตถุcompareTo()สองThreadชิ้นมีความหมายว่าอย่างไร? หนึ่งกระทู้เป็น "มากกว่า" อีกไหม คุณเปรียบเทียบสองArrayList<T>อย่างไร

Objectสัญญานำไปใช้กับทุกคนเรียน Java หากแม้แต่หนึ่งคลาสไม่สามารถเปรียบเทียบกับอินสแตนซ์อื่นของคลาสของตัวเองได้ดังนั้นจึงObjectไม่สามารถกำหนดให้เป็นส่วนหนึ่งของอินเทอร์เฟซได้

โจชัวโบลชใช้คำสำคัญ "การสั่งซื้อธรรมชาติ" เมื่ออธิบายว่าทำไมComparableชั้นเรียนอาจต้องการที่จะดำเนินการ ไม่ใช่ทุกคลาสที่มีลำดับอย่างเป็นธรรมชาติตามที่ฉันได้กล่าวไว้ในตัวอย่างด้านบนดังนั้นทุกคลาสไม่ควรใช้ComparableหรือควรObjectมีcompareToวิธีการ

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

Java ที่มีประสิทธิภาพ, Second Edition : Joshua Bloch รายการ 12 หน้า 62 จุดไข่ปลาลบการอ้างอิงถึงบทอื่น ๆ และตัวอย่างรหัส

สำหรับกรณีที่คุณไม่ต้องการที่จะกำหนดสั่งในที่ไม่ใช่Comparableระดับที่ไม่ได้สั่งธรรมชาติคุณสามารถจัดหาComparatorตัวอย่างเช่นความช่วยเหลือเรียง


3
มีความสนุกสนานมากขึ้นเมื่อคุณเริ่มคิดเกี่ยวกับการเปรียบเทียบกับข้อยกเว้น (ซึ่งไม่ใช่คลาสนามธรรมและดังนั้นจึงมีการใช้งานมันหมายถึง aNullPointerException.compareTo (anUnsupportedFlavorException) จะมี ... ความหมาย?

10
วัตถุทั้งหมดสามารถตรวจสอบความเท่าเทียมกันได้ใน Java ใช่ แต่โดยทั่วไปแล้ว มีตัวอย่างบางส่วนของวัตถุที่การเปรียบเทียบความเท่าเทียมกันไม่สมเหตุสมผล (ไม่ใช่แม้แต่การอ้างอิงความเท่าเทียมกัน) - เช่นซิงเกิลตัน อาจมีอินเตอร์เฟส (คลาสนามธรรม) เช่น ValueEquality และ ReferenceEquality มันอาจจะไม่ใช่ความคิดที่เลวร้ายนัก ...
qbd

5
"วัตถุทั้งหมดสามารถตรวจสอบความเท่าเทียมกันได้หากไม่มีสิ่งใดอีกใครสามารถดูได้ว่ามีวัตถุสองรายการอยู่ในตำแหน่งเดียวกันในหน่วยความจำ (ความเท่าเทียมกันอ้างอิง)" - เนื่องจากเรามี==สำหรับหลังนี่จึงมีวงแหวนกลวงอยู่ โดยไม่คำนึงถึงค่าเริ่มต้นที่ซ้ำซ้อนเราสามารถหาเหตุผลที่ถูกต้องไม่ให้ใช้equalsกับคลาสทั้งหมดเนื่องจากบางประเภทอาจไม่สนับสนุนความสัมพันธ์ที่เท่าเทียมกัน
ราฟาเอล

3
ตัวอย่างสองประเภทที่ไม่สมเหตุสมผลในการกำหนดความเท่าเทียมกัน: สตรีม (เช่นรายการที่ไม่มีที่สิ้นสุดที่ขี้เกียจหรือตัวเลขที่มีความแม่นยำไม่ จำกัด ) และฟังก์ชัน อดีตมีปัญหาว่าการสร้างความเท่าเทียมอาจต้องการการเปรียบเทียบอย่างไม่ จำกัด การตัดสินใจว่าสองฟังก์ชันเท่ากันนั้นไม่สามารถตัดสินใจได้ การถามว่าอินสแตนซ์เหล่านี้มีอยู่สองประเภทในตำแหน่งหน่วยความจำเดียวกันคือ 1) ไม่มีประโยชน์มากและ 2) อนุญาตให้ไคลเอนต์เขียนโค้ดที่มีความอ่อนไหวต่อสิ่งที่ควรนำมาใช้ในรายละเอียด วันนี้ฉันอาจให้ตัวอย่างใหม่ของรายการที่ไม่มีที่สิ้นสุดเหมือนกันทุกครั้งที่คุณถามพรุ่งนี้ฉันอาจจะบันทึก
Doval

6
@Snowman ความจริงที่ว่ามันไร้ประโยชน์เมื่อรวมกับความจริงที่ว่ารายละเอียดการใช้งานนั้นมีเหตุผลเพียงพอที่จะไม่อนุญาต ค่อนข้างมากทุกคลาส "อิงตามมูลค่า" ใน Java 8 มีบางส่วนที่กล่าวว่า "เราไม่รับผิดชอบต่อสิ่งที่เกิดขึ้นถ้าคุณใช้==" เพราะวิธีที่คลาสเหล่านั้นถูกสร้างอินสแตนซ์เป็นรายละเอียดการใช้งาน แต่ภาษาไม่สามารถซ่อนได้ คุณสามารถพูดได้ว่าใครก็ตามที่เปรียบเทียบสองIntegers โดยการอ้างอิงนั้นเป็นคนงี่เง่า แต่การอนุญาตให้เริ่มต้นเปรียบเทียบนั้นยังคงโง่อยู่
Doval

8

JLS §4.3.2กำหนดclassวัตถุในลักษณะต่อไปนี้:

4.3.2 วัตถุคลาส

ห้องเรียน Objectเป็นซูเปอร์คลาส (§8.1.4) ของคลาสอื่นทั้งหมด

คลาสทั้งหมดและประเภทอาเรย์รับช่วง (§8.4.8) วิธีการของคลาสObjectซึ่งสรุปได้ดังนี้:

  • วิธีการที่cloneใช้ในการทำซ้ำของวัตถุ

  • วิธีการequalsกำหนดความคิดของความเท่าเทียมกันของวัตถุซึ่งขึ้นอยู่กับมูลค่าไม่ใช่การอ้างอิงการเปรียบเทียบ

  • วิธีการfinalizeทำงานก่อนวัตถุถูกทำลาย ((12.6)

  • วิธีการgetClassส่งกลับวัตถุระดับที่แสดงถึงระดับของวัตถุ

  • Classวัตถุอยู่สำหรับการอ้างอิงแต่ละประเภท ตัวอย่างเช่นสามารถใช้เพื่อค้นหาชื่อที่มีคุณสมบัติครบถ้วนของคลาสสมาชิกคลาสซูเปอร์คลาสทันทีและอินเตอร์เฟสใด ๆ ที่ใช้

    ชนิดของนิพจน์การเรียกใช้เมธอดgetClassคือClass<? extends |T|>ที่ค้นหาTคลาสหรืออินเทอร์เฟซ (§15.12.1)getClassสำหรับ

    วิธีการเรียนที่ประกาศไว้synchronized(§8.4.3.6) ประสานบนจอภาพที่เกี่ยวข้องกับวัตถุคลาสของคลาส

  • วิธีการที่hashCodeเป็นประโยชน์อย่างมากพร้อมกับเท่ากับวิธีการใน hashtables java.util.Hashmapเช่น

  • วิธีการwait, notifyและnotifyAllถูกนำมาใช้ในการเขียนโปรแกรมพร้อมกันโดยใช้หัวข้อ (§17.2)

  • วิธีการtoStringส่งกลับการเป็นตัวแทนสตริงของวัตถุ

นั่นคือเหตุผลที่equalsมีObjectแต่compareToอยู่ในอินเทอร์เฟซแยกต่างหาก ฉันจะคาดเดาว่าพวกเขาต้องการให้Objectน้อยที่สุดเท่าที่จะทำได้ พวกเขาอาจจะคิดว่าเกือบทุกคน Objectsจะต้องการequalsและhashCode(ซึ่งเป็นเพียงรูปแบบของการทดสอบความเท่าเทียมกัน) แต่วัตถุไม่ทั้งหมดจะต้องมีแนวคิดของการสั่งซื้อซึ่งเป็นสิ่งที่compareToใช้สำหรับ


ผมคิดว่ามีทางทฤษฎีอาจจะเป็นอินเตอร์เฟซEquitable<T>แต่ถ้านำมาใช้มันแล้วทุกระดับจะเป็นObject Equitable<Object>ณ จุดนั้นมีความแตกต่าง? ผลไม่ซับซ้อนจริงๆใช่
Captain Man

1
@CaptainMan ใน. Net objectมีEquals(object)เหมือนใน Java แต่ยังมีIEquatable<T>อินเตอร์เฟส แม้ว่าเหตุผลหลักสำหรับการมีอยู่คือการหลีกเลี่ยงการชกมวยเมื่อTเป็นประเภทค่าซึ่งเป็นไปไม่ได้ใน Java
svick

hashCode ไม่ใช่รูปแบบของการทดสอบความเท่าเทียมกันเนื่องจากมีการชนกันของแฮช ถ้า A และ B เท่ากันพวกเขามี hashCode เดียวกัน แต่ถ้า A และ B มี hashCode เดียวกันนี่ไม่ได้หมายความว่าพวกมันเท่ากัน!
Josef

จริง ๆ แล้วคำตอบของคุณจะได้รับประโยชน์อย่างมากจากการเปลี่ยนไปใช้ JLS รุ่นเก่า ( titanium.cs.berkeley.edu/doc/java-langspec-1.0.pdf ) - มันมีการอ้างอิงที่ดีกว่ามากเกี่ยวกับเหตุผลที่equalsถูกประกาศObjectโดยตรง: The methods equals and hashCode are declared for the benefit of hashtables such as java.util.Hashtable (§21.7)- ตามการออกแบบ Java สามารถใช้งานร่วมกับการออกแบบ Java 1.0 ได้ย้อนหลังเป็นเหตุผลที่แท้จริงสำหรับequalsการอยู่ในที่
vaxquis

เนื่องจากการแก้ไขที่ฉันเสนออาจจะถูกปฏิเสธเนื่องจากเป็น "รุนแรงเกินไป" ในกรณีที่คุณต้องการเพียงแค่ Ctrl + C / Ctrl + V เนื้อหาที่เกี่ยวข้องในคำตอบของคุณ: pastebin.com/8c4EpLRX
vaxquis

2

นอกจากคำตอบที่ยอดเยี่ยมของ Snowman โปรดจำไว้ว่าComparableเป็นอินเทอร์เฟซทั่วไปมาเป็นเวลานาน ชนิดที่ไม่ใช้compareTo(object)ก็นำไปปฏิบัติcompareTo(T)ที่Tเป็นประเภทของตัวเอง สิ่งนี้ไม่สามารถนำไปใช้ได้objectเนื่องจากobjectไม่ทราบคลาสที่จะได้รับ

objectจะได้ได้กำหนดcompareTo(object)วิธีการ แต่นี้จะได้รับอนุญาตให้ไม่ได้เป็นเพียงสิ่งที่มนุษย์หิมะชี้ให้เห็นการเปรียบเทียบระหว่างสองArrayList<T>หรือระหว่างสองThreads แต่แม้การเปรียบเทียบระหว่างและArrayList<T> Threadนั่นไร้สาระมากขึ้น


0

สมมติว่าฉันมีการอ้างอิงวัตถุสองรายการ: X ระบุอินสแตนซ์ของการStringเก็บเนื้อหา "George"; Y ระบุตัวอย่างของการPointถือพิกัด [12,34] ลองพิจารณาคำถามสองข้อต่อไปนี้:

  • X และ Y ระบุวัตถุที่เทียบเท่าหรือไม่

  • X ควรเรียงลำดับก่อนหลังหรือเทียบเท่ากับ Y หรือไม่

ความจริงที่ว่า X และ Y ระบุอินสแตนซ์ของชนิดที่ไม่เกี่ยวข้องไม่ก่อให้เกิดปัญหาใด ๆ เมื่อพิจารณาคำถามแรก วัตถุสามารถถูกพิจารณาว่าเทียบเท่าได้หากประเภทของพวกเขาแบ่งปันฐานร่วมกันที่กำหนดว่าพวกเขาจะเทียบเท่า; ตั้งแต่StringและPointไม่มีฐาน (ประเภทฐานทั่วไปเท่านั้นที่เกี่ยวข้องกับวัตถุที่แตกต่างกันทั้งหมดที่ไม่เทียบเท่า) คำตอบก็คือ "ไม่"

ความจริงที่ว่าประเภทนั้นไม่เกี่ยวข้องกัน แต่ก่อให้เกิดปัญหาใหญ่สำหรับคำถามที่สอง บางประเภทกำหนดความสัมพันธ์ในการสั่งซื้อระหว่างอินสแตนซ์ของพวกเขาและความสัมพันธ์ของการสั่งซื้อบางอย่างอาจขยายไปถึงหลายประเภท [เช่นมันจะเป็นไปได้สำหรับBigIntegerและBigDecimalเพื่อกำหนดวิธีการเปรียบเทียบที่จะอนุญาตให้อินสแตนซ์ของ เป็นไปไม่ได้ที่จะใช้อินสแตนซ์โดยพลการสองกรณีและถามว่า "ควรจัดเรียง X ก่อน, หลังหรือเทียบเท่ากับ Y" และรับการสั่งซื้อทั้งหมด มันจะเป็นไปได้ที่จะถามว่า "ควรจัดเรียง X ก่อนหลังเทียบเท่าหรือ unranked ด้วยความเคารพต่อ Y" ถ้าวัตถุจะต้องรายงานการสั่งซื้อที่สอดคล้องกันแม้ว่าจะไม่รวมหนึ่ง แต่อัลกอริธึมการเรียงลำดับส่วนใหญ่ต้องการลำดับทั้งหมด ดังนั้นแม้ว่าวัตถุทั้งหมดสามารถนำมาใช้compareToวิธีการถ้า "unranked" เป็นผลตอบแทนที่ถูกต้องวิธีการดังกล่าวจะไม่เป็นประโยชน์พอที่จะพิสูจน์การดำรงอยู่ของมัน

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