可能重复:
java Enum definition
更好的制定问题,不被视为重复:
What would be different in Java if Enum declaration didn't have the recursive part
如果语言设计师只使用Enum< E extends Enum>这将如何影响语言?
现在唯一的区别就是有人写了
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;”会限制?
答案 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");