在Java中,枚举类型实现了Comparable接口。很遗憾无法覆盖Comparable的compareTo方法,因为它被标记为final。Enum的默认自然顺序是按枚举列表中的顺序排列。
有人知道为什么Java中的枚举类型有这个限制吗?
有人知道为什么Java中的枚举类型有这个限制吗?
为了保持一致性,当你看到一个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 comparator = new MyEnumComparator();
int order = comparator.compare(MyEnum.CAT, MyEnum.DOG);
或者将其用于集合或数组中:
NavigableSet<MyEnum> set = new TreeSet<MyEnum>(comparator);
MyEnum[] array = MyEnum.values();
Arrays.sort(array, comparator);
更多信息:
MyEnumComparator
没有状态,因此它应该只是一个单例,特别是如果您正在执行 @Bombe 建议的操作;相反,您可以执行类似 MyEnumComparator.INSTANCE.compare(enum1, enum2)
的操作,以避免不必要的对象创建。 - kbolinoLENGTH_COMPARATOR
静态字段中。这样,任何使用该枚举的人都可以轻松找到它。 - Liicompare
方法有些困惑,因为它有两个连续的return
语句。请问你能解释一下吗? - Sam提供一个使用源代码顺序的compareTo的默认实现是可以的;将其设置为final是Sun犯的一个错误。ordinal已经考虑了声明顺序。我同意,在大多数情况下,开发人员只需要逻辑地对他们的元素进行排序,但有时候人们希望按照一种使可读性和维护性最重要的方式组织源代码。例如:
//===== SI BYTES (10^n) =====//
/** 1,000 bytes. */ KILOBYTE (false, true, 3, "kB"),
/** 10<sup>6</sup> bytes. */ MEGABYTE (false, true, 6, "MB"),
/** 10<sup>9</sup> bytes. */ GIGABYTE (false, true, 9, "GB"),
/** 10<sup>12</sup> bytes. */ TERABYTE (false, true, 12, "TB"),
/** 10<sup>15</sup> bytes. */ PETABYTE (false, true, 15, "PB"),
/** 10<sup>18</sup> bytes. */ EXABYTE (false, true, 18, "EB"),
/** 10<sup>21</sup> bytes. */ ZETTABYTE(false, true, 21, "ZB"),
/** 10<sup>24</sup> bytes. */ YOTTABYTE(false, true, 24, "YB"),
//===== IEC BYTES (2^n) =====//
/** 1,024 bytes. */ KIBIBYTE(false, false, 10, "KiB"),
/** 2<sup>20</sup> bytes. */ MEBIBYTE(false, false, 20, "MiB"),
/** 2<sup>30</sup> bytes. */ GIBIBYTE(false, false, 30, "GiB"),
/** 2<sup>40</sup> bytes. */ TEBIBYTE(false, false, 40, "TiB"),
/** 2<sup>50</sup> bytes. */ PEBIBYTE(false, false, 50, "PiB"),
/** 2<sup>60</sup> bytes. */ EXBIBYTE(false, false, 60, "EiB"),
/** 2<sup>70</sup> bytes. */ ZEBIBYTE(false, false, 70, "ZiB"),
/** 2<sup>80</sup> bytes. */ YOBIBYTE(false, false, 80, "YiB");
以上排序在源代码中看起来不错,但是作者认为compareTo应该按字节数排序。实现这种排序的源代码会降低代码组织性。
作为枚举类型的客户端,我并不关心作者如何组织他们的源代码。但我希望他们的比较算法有一定的意义。Sun公司让源代码编写者处于了困境中。
枚举值按照声明的顺序进行逻辑排序。这是Java语言规范的一部分。因此,只有在同一个枚举类型中的成员才能进行比较。规范进一步保证了compareTo()返回的可比较顺序与声明值的顺序相同。这就是枚举的定义。
一个可能的解释是,compareTo
应该与 equals
保持一致。
而且枚举类型的 equals
应该与标识相等性 (==
) 保持一致。
如果 compareTo
不是 final 的话,就有可能将其覆盖为与 equals
不一致的行为,这将非常违反直觉。
如果你想改变枚举元素的自然顺序,可以在源代码中改变它们的顺序。