เหตุใดจึงใช้ CompareTo บน Enum ขั้นสุดท้ายใน Java


95

enum ใน Java ใช้Comparableอินเทอร์เฟซ ก็จะได้รับความสุขที่จะแทนที่Comparable's compareToวิธี แต่ที่นี่ก็ทำเครื่องหมายเป็นครั้งสุดท้าย ลำดับธรรมชาติเริ่มต้นEnumของcompareToคือลำดับที่ระบุไว้

ไม่มีใครรู้ว่าทำไม Java enums ถึงมีข้อ จำกัด นี้?


มีคำอธิบายที่ดีมากใน Effective Java - 3rd Edition ใน Item 10 (การจัดการกับ equals () แต่ในรายการ 14 พวกเขาบอกปัญหากับ CompareTo () เหมือนกัน) กล่าวโดยย่อ: หากคุณขยายคลาสที่สร้างได้ทันที (เช่น enum) และเพิ่มองค์ประกอบค่าคุณจะไม่สามารถรักษาสัญญาเท่ากับ (หรือเปรียบเทียบกับ) ได้
Christian H. Kuhn

คำตอบ:


121

เพื่อความมั่นคงผมคิดว่า ... เมื่อคุณเห็นenumชนิดที่คุณรู้สำหรับข้อเท็จจริงที่สั่งซื้อตามธรรมชาติของมันคือคำสั่งซื้อที่คงที่มีการประกาศ

ในการแก้ปัญหานี้คุณสามารถสร้างของคุณเองComparator<MyEnum>และใช้งานได้อย่างง่ายดายเมื่อใดก็ตามที่คุณต้องการลำดับที่แตกต่างกัน:

enum MyEnum
{
    DOG("woof"),
    CAT("meow");

    String sound;    
    MyEnum(String s) { sound = s; }
}

class MyEnumComparator implements Comparator<MyEnum>
{
    public int compare(MyEnum o1, MyEnum o2)
    {
        return -o1.compareTo(o2); // this flips the order
        return o1.sound.length() - o2.sound.length(); // this compares length
    }
}

คุณสามารถใช้Comparatorโดยตรง:

MyEnumComparator c = new MyEnumComparator();
int order = c.compare(MyEnum.CAT, MyEnum.DOG);

หรือใช้ในคอลเลกชันหรืออาร์เรย์:

NavigableSet<MyEnum> set = new TreeSet<MyEnum>(c);
MyEnum[] array = MyEnum.values();
Arrays.sort(array, c);    

ข้อมูลเพิ่มเติม:


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

7
ใช่. MyEnumComparator.compare ใหม่ (enum1, enum2) Et voilá.
Bombe

@martinoconnor & Bombe: ฉันได้รวมความคิดเห็นของคุณไว้ในคำตอบแล้ว ขอบคุณ!
Zach Scrivena

เนื่องจากMyEnumComparatorไม่มีสถานะจึงควรเป็นซิงเกิลตันโดยเฉพาะอย่างยิ่งหากคุณกำลังทำสิ่งที่ @Bombe แนะนำ แต่คุณจะทำบางอย่างMyEnumComparator.INSTANCE.compare(enum1, enum2)เพื่อหลีกเลี่ยงการสร้างวัตถุที่ไม่จำเป็น
kbolino

2
@kbolino: ตัวเปรียบเทียบเหตุการณ์อาจเป็นคลาสที่ซ้อนกันภายในคลาส enum สามารถเก็บไว้ในLENGTH_COMPARATORฟิลด์คงที่ใน enum ด้วยวิธีนี้จะเป็นเรื่องง่ายที่จะหาสำหรับทุกคนที่ใช้ enum
Lii

40

ให้การใช้งานค่าเริ่มต้นของ CompareTo ที่ใช้ลำดับซอร์สโค้ดนั้นใช้ได้ การทำให้สุดท้ายเป็นความผิดพลาดในส่วนของซัน ลำดับบัญชีสำหรับคำสั่งประกาศแล้ว ฉันยอมรับว่าในสถานการณ์ส่วนใหญ่นักพัฒนาสามารถจัดลำดับองค์ประกอบได้อย่างมีเหตุผล แต่บางครั้งก็ต้องการให้ซอร์สโค้ดจัดระเบียบในลักษณะที่ทำให้การอ่านง่ายและการบำรุงรักษาเป็นสิ่งสำคัญยิ่ง ตัวอย่างเช่น:


  //===== SI BYTES (10^n) =====//

  /** 1,000 bytes. */ KILOBYTE (false, true,  3, "kB"),
  /** 106 bytes. */   MEGABYTE (false, true,  6, "MB"),
  /** 109 bytes. */   GIGABYTE (false, true,  9, "GB"),
  /** 1012 bytes. */  TERABYTE (false, true, 12, "TB"),
  /** 1015 bytes. */  PETABYTE (false, true, 15, "PB"),
  /** 1018 bytes. */  EXABYTE  (false, true, 18, "EB"),
  /** 1021 bytes. */  ZETTABYTE(false, true, 21, "ZB"),
  /** 1024 bytes. */  YOTTABYTE(false, true, 24, "YB"),

  //===== IEC BYTES (2^n) =====//

  /** 1,024 bytes. */ KIBIBYTE(false, false, 10, "KiB"),
  /** 220 bytes. */   MEBIBYTE(false, false, 20, "MiB"),
  /** 230 bytes. */   GIBIBYTE(false, false, 30, "GiB"),
  /** 240 bytes. */   TEBIBYTE(false, false, 40, "TiB"),
  /** 250 bytes. */   PEBIBYTE(false, false, 50, "PiB"),
  /** 260 bytes. */   EXBIBYTE(false, false, 60, "EiB"),
  /** 270 bytes. */   ZEBIBYTE(false, false, 70, "ZiB"),
  /** 280 bytes. */   YOBIBYTE(false, false, 80, "YiB");

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

ในฐานะลูกค้าของการแจงนับฉันไม่สนใจวิธีการจัดระเบียบซอร์สโค้ดของผู้เขียน ฉันต้องการให้อัลกอริทึมการเปรียบเทียบของพวกเขามีเหตุผลบางอย่าง Sun ใส่ตัวเขียนซอร์สโค้ดโดยไม่จำเป็น


4
เห็นด้วยฉันต้องการให้ enum ของฉันสามารถมี algorythm ที่เทียบเคียงได้ทางธุรกิจแทนที่จะเป็นคำสั่งซื้อซึ่งอาจถูกทำลายได้หากไม่มีใครให้ความสนใจ
TheBakker

6

ค่าการแจงนับจะเรียงลำดับอย่างแม่นยำตามลำดับที่ประกาศไว้ นี่เป็นส่วนหนึ่งของข้อกำหนดภาษา Java ดังนั้นจึงเป็นไปตามที่ค่าการแจงนับสามารถเปรียบเทียบได้ก็ต่อเมื่อเป็นสมาชิกของ Enum เดียวกัน ข้อกำหนดต้องการรับประกันเพิ่มเติมว่าลำดับการเทียบเคียงที่ส่งคืนโดย CompareTo () จะเหมือนกับลำดับที่ประกาศค่า นี่คือคำจำกัดความของการแจงนับ


ดังที่ Thomas Paine อธิบายไว้อย่างชัดเจนในตัวอย่างของเขาภาษาสามารถเรียงลำดับตามวากยสัมพันธ์เท่านั้นไม่ใช่ความหมาย คุณบอกว่ารายการถูกเรียงลำดับตามเหตุผล แต่วิธีที่ฉันเข้าใจ enum รายการนั้นถูกห่อหุ้มด้วยวิธีเชิงตรรกะ
Bondax

2

หนึ่งคำอธิบายที่เป็นไปได้คือควรจะสอดคล้องกับcompareToequals

และequalsสำหรับ enums ควรสอดคล้องกับความเท่าเทียมกันของข้อมูลประจำตัว ( ==)

หากcompareToที่ใดไม่เป็นที่สิ้นสุดจะเป็นไปได้ที่จะแทนที่ด้วยพฤติกรรมที่ไม่สอดคล้องกับequalsซึ่งจะเป็นการตอบโต้ที่ใช้งานง่ายมาก


แม้ว่าจะแนะนำว่า CompareTo () สอดคล้องกับ equals () แต่ก็ไม่จำเป็น
Christian H. Kuhn

-1

หากคุณต้องการเปลี่ยนลำดับตามธรรมชาติขององค์ประกอบของ enum ให้เปลี่ยนลำดับในซอร์สโค้ด


ใช่นั่นคือสิ่งที่ฉันเขียนไว้ในรายการต้นฉบับ :)
neu242

ใช่ แต่คุณไม่ได้อธิบายว่าทำไมคุณถึงต้องการลบล้าง CompareTo () ดังนั้นข้อสรุปของฉันก็คือคุณกำลังพยายามทำ Something Bad ™และฉันพยายามแสดงวิธีที่ถูกต้องมากขึ้น
Bombe

ฉันไม่เห็นว่าทำไมฉันต้องเรียงลำดับรายการด้วยมือในเมื่อคอมพิวเตอร์ทำได้ดีกว่าฉันมาก
neu242

ในการแจงนับจะถือว่าคุณสั่งซื้อรายการในลักษณะเฉพาะนั้นด้วยเหตุผล หากไม่มีเหตุผลใดที่คุณควรใช้ <enum> .toString (). CompareTo () หรืออาจจะมีอะไรที่แตกต่างออกไปโดยสิ้นเชิงเช่นอาจจะเป็นชุด?
Bombe

เหตุผลนี้ก็คือฉันมี Enum ที่มี {isocode, countryname} มีการจัดเรียงชื่อประเทศด้วยตนเองซึ่งทำงานได้ตามที่ตั้งใจไว้ จะเกิดอะไรขึ้นถ้าฉันตัดสินใจว่าฉันต้องการจัดเรียงไอโซโทปต่อจากนี้?
neu242
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.