可能重复:
java Enum定义
更好的公式化问题,不被认为是重复的:
如果Enum声明没有递归部分,那么在Java中会有什么不同
如果语言设计师只使用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 <E扩展MyEnum <E >>"会限制?
And*_*yle 38
这是一个常见的问题,也是可以理解的.看一下这个部分的泛型常见问题解答(实际上,尽可能多地阅读整篇文档,它做得很好,内容丰富).
简短的回答是它迫使班级自身参数化; 这是超类使用泛型参数定义方法所必需的,这些方法透明地工作("本机",如果你愿意)和它们的子类.
编辑:作为一个(非)示例,请考虑clone()
方法Object
.目前,它被定义为返回类型的值Object
.由于协变返回类型,特定的子类可以(并且经常)定义它们返回更具体的类,但是这不能强制执行,因此无法推断任意类.
现在,如果 Object被定义为Enum,Object<T extends Object<T>>
那么你必须将所有类定义为类似的东西public class MyFoo<MyFoo>
.因此,clone()
可以声明返回一种类型,T
并且您可以在编译时确保返回的值始终与对象本身完全相同(甚至子类也不会匹配参数).
现在在这种情况下,Object没有像这样参数化,因为当99%的人根本不打算使用它时,将这个行李放在所有类别上是非常烦人的.但对于某些类层次结构,它可能非常有用 - 我之前使用过类似的技术,使用了具有多个实现的抽象,递归表达式解析器类型.这种结构使得编写"显而易见"的代码成为可能,而无需在任何地方进行转换,或者只是为了更改具体的类定义而进行复制和粘贴.
编辑2(实际回答你的问题!):
如果Enum被定义为Enum<E extends Enum>
,那么正如你正确地说,有人可以将类定义为A extends Enum<B>
.这违背了泛型构造的要点,即确保泛型参数始终是所讨论的类的确切类型.举一个具体的例子,Enum将其compareTo方法声明为
public final int compareTo(E o)
Run Code Online (Sandbox Code Playgroud)
在这种情况下,由于您定义A
为扩展Enum<B>
,A
因此只能将实例与B
(无论B是什么)的实例进行比较,这几乎肯定不是很有用. 使用附加构造,您知道任何扩展Enum的类只能与其自身进行比较.因此,您可以在超类中提供在所有子类中保持有用且特定的方法实现.
(如果没有这种递归泛型技巧,唯一的另一种选择就是将compareTo定义为public final int compareTo(Enum o)
.这实际上并不是同一个东西,因为java.math.RoundingMode
在java.lang.Thread.State
没有编译器抱怨的情况下可以将a 与a 进行比较,这也不是很有用.)
好吧,让我们远离Enum
自己,因为我们似乎已经挂断了它.相反,这是一个抽象类:
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
}
}
Run Code Online (Sandbox Code Playgroud)
我们将有几个具体的实现 - SaveToDatabaseManipulator,SpellCheckingManipulator,等等.此外,我们还想让人们定义自己的,因为这是一个超级有用的类.;-)
现在 - 你会注意到我们正在使用递归泛型定义,然后T
从createChild
方法返回.这意味着:
1)我们知道并且编译器知道如果我打电话:
SpellCheckingManipulator obj = ...; // We have a reference somehow
return obj.createChild();
Run Code Online (Sandbox Code Playgroud)
那么返回的值肯定是a SpellCheckingManipulator
,即使它使用了超类中的定义.这里的递归泛型允许编译器知道对我们来说什么是显而易见的,因此您不必继续转换返回值(例如,您经常需要这样做clone()
).
2)请注意,我没有声明方法final,因为某些特定的子类可能希望用自己更合适的版本覆盖它.泛型定义意味着无论是谁创建新类或如何定义它,我们仍然可以断言从eg返回BrandNewSloppilyCodedManipulator.createChild()
仍将是一个实例BrandNewSloppilyCodedManipulator
.如果粗心的开发人员试图将其定义为仅返回Manipulator
,编译器将不会让它们.如果他们试图将类定义为BrandNewSloppilyCodedManipulator<SpellCheckingManipulator>
,它也不会让它们.
基本上,结论是当你想在超类中提供一些在子类中更具体的特性时,这个技巧很有用.通过声明这样的超类,您将锁定任何子类的泛型参数作为子类本身.这就是为什么你可以在超类中编写泛型compareTo
或createChild
方法,并防止它在你处理特定的子类时变得过于模糊.