Чому compareTo на фіналі Enum у Java?


93

Enum в Java реалізує Comparableінтерфейс. Було б добре , щоб перевизначити Comparable«s compareToметод, але тут він позначений як остаточний. Природний порядок по замовчуванням на Enum«s compareToє перерахованим порядком.

Хтось знає, чому перелічення Java мають це обмеження?


Є дуже точне пояснення в Effective Java - 3rd Edition у пункті 10 (що стосується рівних (), але в пункті 14 вони говорять, що проблема з compareTo () однакова). Коротше кажучи: Якщо ви розширюєте примірний клас (наприклад, перерахування) і додаєте компонент значення, ви не можете зберегти контракт equals (або compareTo).
Крістіан Х. Кун

Відповіді:


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 до колекції. Це не так сильно допомагає, якщо ви хочете зробити пряме порівняння.
Мартін Оконнор

7
Так. новий MyEnumComparator.compare (enum1, enum2). Et voilá.
Bombe

@martinoconnor & Bombe: Я включив ваші коментарі у відповідь. Дякую!
Zach Scrivena

Оскільки MyEnumComparatorне має штату, він повинен бути просто одиночним, особливо якщо ви робите те, що пропонує @Bombe; натомість ви зробите щось на зразок MyEnumComparator.INSTANCE.compare(enum1, enum2)уникнення непотрібного створення об’єктів
kbolino

2
@kbolino: Подією порівняння може бути вкладений клас всередині класу перерахування. Його можна зберігати у LENGTH_COMPARATORстатичному полі в переліку. Таким чином було б легко знайти кожного, хто використовує перерахування.
Lii

39

Забезпечення реалізації за замовчуванням порівнянняTo, яка використовує впорядкування вихідного коду - це нормально; зробити його остаточним було помилковим кроком з боку Сонця. Порядковий номер вже враховує порядок декларування. Я згоден з тим, що у більшості ситуацій розробник може просто логічно впорядковувати свої елементи, але іноді хочеться, щоб вихідний код був організований таким чином, щоб читабельність та обслуговування були першорядними. Наприклад:


  //===== 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");

Вищевказане впорядкування виглядає непогано у вихідному коді, але не так, як вважає автор, слід порівнювати To Бажана поведінка порівнянняДля того, щоб упорядкування здійснювалось за кількістю байтів. Впорядкування вихідного коду, яке могло б зробити це, погіршує організацію коду.

Як клієнту переліку, мені було байдуже, як автор організував їхній вихідний код. Однак я хочу, щоб їх алгоритм порівняння мав якийсь сенс. Sun надто непотрібно поставив розробників вихідного коду.


3
Погодьтеся, я хочу, щоб мій перелік міг мати порівняльний алгоритм бізнесу замість наказу, який можна порушити, якщо хтось не зверне уваги.
TheBakker

6

Значення перерахування точно впорядковуються логічно відповідно до порядку їх оголошення. Це частина специфікації мови Java. Звідси випливає, що значення перерахування можна порівнювати, лише якщо вони є членами одного переліку. Специфікація хоче додатково гарантувати, що порівнянний порядок, який повертає функція compareTo (), такий самий, як і порядок, у якому були оголошені значення. Це саме визначення переліку.


Як ясно показав на своєму прикладі Томас Пейн, мова може впорядковуватись лише за синтаксикою, а не за семантикою. Ви кажете, що елементи впорядковані логічно, але, як я розумію перелік, елементи інкапсулюються логічними засобами.
Bondax

2

Одне з можливих пояснень полягає в тому, що воно compareToповинно відповідатиequals .

А equalsпереліки повинні відповідати рівності ідентичності (== ).

Якщо compareToде не бути остаточним, можна було б замінити його поведінкою, яка не узгоджувалась equals, що було б дуже інтуїтивно зрозумілим.


Хоча рекомендується, щоб функція compareTo () узгоджувалась з equals (), це не обов'язково.
Крістіан Х. Кун

-1

Якщо ви хочете змінити природний порядок елементів вашого переліку, змініть їх порядок у вихідному коді.


Так, це те, що я писав в оригінальній статті :)
neu242

Так, але ви насправді не пояснюєте, чому ви хочете перевизначити функцію compareTo (). Отже, мій висновок був таким, що ти намагаєшся зробити щось погане ™, а я намагався показати тобі більш правильний шлях.
Bombe

Я не розумію, чому мені доводиться сортувати записи вручну, коли комп’ютери роблять це набагато краще за мене.
neu242

У переліченнях передбачається, що ви впорядкували записи саме таким чином з причини. Якщо немає причин, то вам краще скористатися <enum> .toString (). CompareTo (). А може, щось зовсім інше, як, можливо, набір?
Bombe

Причиною цього стало те, що у мене є Enum із {isocode, countryname}. Його було відсортовано вручну за назвою країни, що працює за призначенням. Що робити, якщо я вирішу, що відтепер хочу сортувати за ізокодом?
neu242
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.