Java의 Enum final에 compareTo가있는 이유는 무엇입니까?


95

Java의 열거 형은 Comparable인터페이스를 구현합니다 . ComparablecompareTo메서드 를 재정의하는 것이 좋지만 여기서는 최종적으로 표시됩니다. 의 기본 자연 질서 Enum의는 compareTo나열된 순서입니다.

Java 열거 형에 이러한 제한이있는 이유를 아는 사람이 있습니까?


항목 10의 Effective Java-3rd Edition에는 매우 자세한 설명이 있습니다 (equals ()를 다루지 만 항목 14에서는 compareTo ()의 문제가 동일하다고 말합니다). 즉, 인스턴스화 가능한 클래스 (예 : 열거 형)를 확장하고 값 구성 요소를 추가하면 같음 (또는 compareTo) 계약을 유지할 수 없습니다.
기독교 H. 쿤

답변:


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
네, 그렇습니다. new MyEnumComparator.compare (enum1, enum2). Et voilá.
Bombe

@martinoconnor & Bombe : 귀하의 의견을 답변에 통합했습니다. 감사!
Zach Scrivena

MyEnumComparator상태가 없기 때문에 , 특히 @Bombe가 제안하는 것을하고 있다면 싱글 톤이어야합니다. 같은 대신에, 당신이 뭔가를 할 것입니다 MyEnumComparator.INSTANCE.compare(enum1, enum2)불필요한 객체 생성 방지하기 위해
kbolino

2
@kbolino : 비교기는 enum 클래스 내의 중첩 된 클래스 일 수 있습니다. LENGTH_COMPARATOR열거 형 의 정적 필드에 저장할 수 있습니다 . 그런 식으로 열거 형을 사용하는 사람을 쉽게 찾을 수 있습니다.
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가 작동해야한다고 생각하는 방식은 아닙니다. 원하는 compareTo 동작은 바이트 수를 기준으로 정렬하는 것입니다. 그렇게하는 소스 코드 순서는 코드의 구성을 저하시킵니다.

열거 형의 클라이언트로서 저자가 소스 코드를 어떻게 구성했는지는 신경 쓰지 않았습니다. 나는 그들의 비교 알고리즘이 어떤 종류의 이해를 원합니다. Sun은 불필요하게 소스 코드 작성자를 바인드했습니다.


4
동의합니다. 누군가가주의를 기울이지 않으면 깨질 수있는 주문 대신 내 열거 형이 비즈니스에 필적하는 알고리즘을 가질 수 있기를 바랍니다.
TheBakker 2015-08-26

6

열거 형 값은 선언 된 순서에 따라 논리적으로 정확하게 정렬됩니다. 이것은 Java 언어 사양의 일부입니다. 따라서 열거 형 값은 동일한 Enum의 구성원 인 경우에만 비교할 수 있습니다. 스펙은 compareTo ()에 의해 반환 된 비교 가능한 순서가 값이 선언 된 순서와 동일하다는 것을 추가로 보장하려고합니다. 이것이 바로 열거 형의 정의입니다.


Thomas Paine이 그의 예에서 명확하게 말했듯이 언어는 의미론이 아닌 구문론에 의해서만 정렬 될 수 있습니다. 항목은 논리적으로 정렬 되어 있지만 열거 형을 이해하는 방식으로 항목은 논리적 수단 으로 캡슐화 됩니다.
Bondax

2

한 가지 가능한 설명은와 compareTo일치해야한다는 것입니다 equals.

그리고 equals열거 형은 동일성 ( ==) 과 일치해야합니다 .

경우 compareTo가 아닌 마지막으로 곳에는 일치하지 않았다 행동으로 재정의 할 수있을 것이다 equals매우 직관적 것입니다.


compareTo ()는 equals ()와 일치하는 것이 좋지만 필수는 아닙니다.
기독교 H. 쿤

-1

열거 형 요소의 자연스러운 순서를 변경하려면 소스 코드에서 순서를 변경하십시오.


네, 그것이 제가 원래 항목에 썼던 것입니다 :)
neu242

예, 그러나 compareTo ()를 재정의하려는 이유를 설명하지 않습니다. 그래서 제 결론은 당신이 Something Bad ™를하려고했고 더 정확한 방법을 보여 주려고 노력하고 있다는 것입니다.
Bombe

컴퓨터가 나보다 훨씬 더 잘할 때 왜 내가 손으로 항목을 정렬해야하는지 모르겠습니다.
neu242

열거 형에서는 이유 때문에 특정 방식으로 항목을 주문했다고 가정합니다. 이유가 없다면 <enum> .toString (). compareTo ()를 사용하는 것이 좋습니다. 아니면 세트처럼 완전히 다른 것일 수도 있습니다.
Bombe

이것이 나온 이유는 {isocode, countryname}이있는 Enum이 있기 때문입니다. 의도 한대로 작동하는 국가 이름을 기준으로 수동으로 정렬되었습니다. 지금부터 아이소 코드를 기준으로 정렬하려면 어떻게해야합니까?
neu242
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.