【发布时间】:2017-02-25 23:12:54
【问题描述】:
Enum 中的类型参数E 定义为<E extends Enum<E>>。那么为什么在 Enum 实现源 code 中,我们仍然需要在 compareTo 方法中检查 getClass() 和 getDeclaringClass() 呢?当我在compareTo 中设置不同的枚举类型对象时,我认为编译器无法通过。
【问题讨论】:
Enum 中的类型参数E 定义为<E extends Enum<E>>。那么为什么在 Enum 实现源 code 中,我们仍然需要在 compareTo 方法中检查 getClass() 和 getDeclaringClass() 呢?当我在compareTo 中设置不同的枚举类型对象时,我认为编译器无法通过。
【问题讨论】:
它涵盖了比较原始类型和values obtained via unsafe / unchecked casts and conversions(如Comparable,或Object a)的情况:
static enum Fruit { Apple, Orange, Banana };
static enum Animal { Cat, Dog, Horse };
public static final void main (String[] args) throws Exception {
Enum f = Fruit.Apple;
Enum a = Animal.Cat;
f.compareTo(a);
}
在那里,compareTo 在显式 getDeclaringClass 比较时会失败,ClassCastException,因为它会毫无问题地通过第一个显式转换 (Enum other = (Enum)o)。
至于比较getClass,它在该来源中被标记为“优化”。这是一种优化的原因是,如果value classes 相同,那么它们肯定来自同一个枚举,因此无需调用稍贵的getDeclaringClass。由于绝大多数枚举可能是简单的枚举(没有值类主体),因此针对这种情况进行了优化。
【讨论】:
f 和 a 的类型为 Object,它也适用。
a 为Object。如果f 是Object,那么您将没有f.compareTo 可用。此外,对于Enum 和Object 类型的a,异常堆栈跟踪实际上略有不同,因此在我在此答案中添加Object 的提及之前,我需要考虑如何用文字清楚地表达这一点。
f 转换为Comparable<?>,您可以。 (好吧,Object 是错误的选择……我的错。)另外,请考虑Enum<?>,它在技术上不是原始类型(参见 JLS 4.8)。但我的观点是,“原始类型”并不是唯一的原因。
f 是Comparable<?>,f.compareTo(a); 将如何编译?
如果您使用 Enum 作为原始类型,它可以。例如在这个程序中:
public static void main(String[] args) {
Enum e = A.x; // All occurrences of E are erased to Enum
e.compareTo(B.i); // B.i extends Enum, so this is fine
}
enum A {x,y,z};
enum B {i,j,k};
【讨论】:
E 不是Enum。如果您尝试将Enum 提供为E,它将无法编译。只是没有E。
compareTo(E)的擦除是compareTo(Enum)。