为什么在 Java 中 Enum final 上使用 compareTo?

Java 中的枚举实现 Comparable接口。重写 ComparablecompareTo方法会很好,但是这里它被标记为 final。EnumcompareTo上的默认自然顺序是列出的顺序。

有人知道为什么 Java 枚举有这个限制吗?

76206 次浏览

If you want to change the natural order of your enum’s elements, change their order in the source code.

For consistency I guess... when you see an enum type, you know for a fact that its natural ordering is the order in which the constants are declared.

To workaround this, you can easily create your own Comparator<MyEnum> and use it whenever you need a different ordering:

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
}
}

You can use the Comparator directly:

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

or use it in collections or arrays:

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

Further information:

Enumeration values are precisely ordered logically according to the order they are declared. This is part of the Java language specification. Therefore it follows that enumeration values can only be compared if they are members of the same Enum. The specification wants to further guarantee that the comparable order as returned by compareTo() is the same as the order in which the values were declared. This is the very definition of an enumeration.

Providing a default implementation of compareTo that uses the source-code ordering is fine; making it final was a misstep on Sun's part. The ordinal already accounts for declaration order. I agree that in most situations a developer can just logically order their elements, but sometimes one wants the source code organized in a way that makes readability and maintenance to be paramount. For example:


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

The above ordering looks good in source code, but is not how the author believes the compareTo should work. The desired compareTo behavior is to have ordering be by number of bytes. The source-code ordering that would make that happen degrades the organization of the code.

As a client of an enumeration i could not care less how the author organized their source code. I do want their comparison algorithm to make some kind of sense, though. Sun has unnecessarily put source code writers in a bind.

One possible explanation is that compareTo should be consistent with equals.

And equals for enums should be consistent with identity equality (==).

If compareTo where to be non-final it would be possible to override it with a behaviour which was not consistent with equals, which would be very counter-intuitive.