为什么在java enum中声明为Enum <e extends =“”enum <e =“”>&gt; </e>

时间:2010-06-17 12:55:41

标签: java enums

  

可能重复:
  java Enum definition

  

更好的制定问题,不被视为重复:
  What would be different in Java if Enum declaration didn't have the recursive part

如果语言设计师只使用Enum&lt; E extends Enum&gt;这将如何影响语言?

现在唯一的区别就是有人写了

A extends Enum<B>

但由于java中不允许扩展仍然非法的枚举。 我也在考虑有人向jvm提供一个字节码,将字符串定义为扩展枚举 - 但是泛型不会影响它,因为它们都被删除了。

那么这种宣言的重点是什么?

谢谢!

修改的 为简单起见,我们来看一个例子:

interface MyComparable<T> {
    int myCompare(T o);
}

class MyEnum<E extends MyEnum> implements MyComparable<E> {
    public int myCompare(E o) { return -1; }
}

class FirstEnum extends MyEnum<FirstEnum> {}

class SecondEnum extends MyEnum<SecondEnum> {}

这个类结构有什么问题?可以做什么“MyEnum&lt; E扩展MyEnum&lt; E&gt;&gt;”会限制?

2 个答案:

答案 0 :(得分:36)

这是一个常见问题,可以理解。看一下this part of the generics FAQ的答案(实际上,尽可能多地阅读整篇文档,它做得相当好,内容丰富)。

简短的回答是它强迫班级自身参数化;这是超类使用泛型参数定义方法所必需的,这些方法透明地工作(“本机”,如果你愿意)和它们的子类。

编辑:例如,作为(非)示例,请考虑clone()上的Object方法。目前,它被定义为返回类型Object的值。由于协变返回类型,特定的子类可以(并且经常)定义它们返回更具体的类,但是这不能强制执行,因此无法推断任意类。

现在, if 对象定义为Enum,即Object<T extends Object<T>>,那么您必须将所有类定义为public class MyFoo<MyFoo>之类的内容。因此,可以声明clone()返回T类型,并且您可以在编译时确保返回的值始终与对象本身完全相同(甚至子类也不会与参数匹配)

现在在这种情况下,对象没有像这样参数化,因为当99%的人根本不打算使用它时,将这个行李放在所有课程上会非常烦人。但对于某些类层次结构,它可能非常有用 - 我之前使用过类似的技术,使用了具有多个实现的抽象,递归表达式解析器类型。这种结构使得编写“显而易见”的代码成为可能,而无需在任何地方进行转换,或者只是为了更改具体的类定义而进行复制和粘贴。

编辑2 (要真正回答您的问题!):

如果将枚举定义为Enum<E extends Enum>,那么正如您所说,有人可以将类定义为A extends Enum<B>。这违背了泛型构造的要点,即确保泛型参数始终是所讨论的类的精确类型。举一个具体的例子,Enum将compareTo方法声明为

public final int compareTo(E o)

在这种情况下,由于您定义了A以扩展Enum<B>,因此A的实例只能与B的实例进行比较(无论B是什么),这是几乎肯定不是很有用。 使用附加构造,您知道扩展Enum的任何类只能与其自身进行比较。因此,您可以在超类中提供在所有子类中保持有用且特定的方法实现。

(如果没有这种递归泛型技巧,唯一的另一种选择是将compareTo定义为public final int compareTo(Enum o)。这实际上并不是一回事,因为可以将java.math.RoundingMode与{{1}进行比较没有编译器抱怨,这也不是很有用。)


好吧,让我们离开java.lang.Thread.State本身,因为我们似乎已经挂断了它。相反,这是一个抽象类:

Enum

我们将有几个具体的实现 - SaveToDatabaseManipulator,SpellCheckingManipulator,等等。此外,我们还想让人们定义自己的,因为这是一个超级有用的类。 ; - )

现在 - 您会注意到我们正在使用递归通用定义,然后从public abstract class Manipulator<T extends Manipulator<T>> { /** * This method actually does the work, whatever that is */ public abstract void manipulate(DomainObject o); /** * This creates a child that can be used for divide and conquer-y stuff */ public T createChild() { // Some really useful implementation here based on // state contained in this class } } 方法返回T。这意味着:

1)我们知道,编译器知道,如果我打电话:

createChild

然后返回的值肯定是SpellCheckingManipulator obj = ...; // We have a reference somehow return obj.createChild(); ,即使它使用了超类中的定义。这里的递归泛型允许编译器知道对我们来说什么是显而易见的,因此您不必继续转换返回值(例如,您通常与SpellCheckingManipulator有关)。

2)请注意,我没有声明方法final,因为某些特定的子类可能希望用更适合自己的版本覆盖它。泛型定义意味着无论是谁创建新类或如何定义它,我们仍然可以断言从例如clone()仍然是BrandNewSloppilyCodedManipulator.createChild()的实例。如果粗心的开发人员试图将其定义为只返回BrandNewSloppilyCodedManipulator,编译器将不会让它们。如果他们尝试将定义为Manipulator,它也不会让它们。

基本上,结论是当你想在超类中提供一些在子类中更具体的特性时,这个技巧很有用。通过声明这样的超类,您锁定任何子类的泛型参数作为子类本身。这就是为什么你可以在超类中编写通用的BrandNewSloppilyCodedManipulator<SpellCheckingManipulator>compareTo方法,并防止它在你处理特定的子类时变得过于模糊。

答案 1 :(得分:1)

  

现在唯一的区别就是有人写了

A extends Enum<B>
     

但由于java中不允许扩展仍然非法的枚举。我也在考虑有人向jvm提供一个字节码,将字符串定义为扩展枚举 - 但是泛型不会影响它,因为它们都被删除了。

你是对的,语言会阻止某人这样做。但是,使其成为E extends Enum<E>而不仅仅是extends Enum的好处是Enum上的某些方法现在将返回正确的特定类型。

海报要求提供一些不起作用的东西。如果将Enum定义为Enum<E>,那么您可以执行此操作:

// Would be legal, because element types of `DaysOfWeek` and `Color` both
//   extend `Enum<E>`, not `Enum<E extends Enum<E>>`.
DaysOfWeek d = Enum.valueOf(Color.class, "Purple");