这是一个常见问题,可以理解。看看this part of the generics FAQ 的答案(实际上,尽可能多地阅读整个文档,因为它做得很好且内容丰富)。
简短的回答是它强制类在自身上进行参数化;这是超类使用泛型参数定义方法所必需的,这些方法与它们的子类透明地工作(“本机”,如果你愿意的话)。
编辑:作为一个(非)示例,考虑Object 上的clone() 方法。目前,它被定义为返回一个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>。这违背了泛型构造的要点,即确保泛型参数始终是所讨论类的 exact 类型。举一个具体的例子,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 与 java.lang.Thread.State 进行比较而编译器不会抱怨,这又不是很有用。)
好的,让我们远离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
}
}
我们将有几个具体的实现——SaveToDatabaseManipulator、SpellCheckingManipulator 等等。此外,我们还希望让人们定义自己的,因为这是一个超级有用的类。 ;-)
现在 - 您会注意到我们正在使用递归泛型定义,然后从 createChild 方法返回 T。这意味着:
1) 我们知道并且编译器知道如果我调用:
SpellCheckingManipulator obj = ...; // We have a reference somehow
return obj.createChild();
那么返回值肯定是SpellCheckingManipulator,即使它使用的是超类中的定义。这里的递归泛型允许编译器知道什么对我们来说是显而易见的,因此您不必继续强制转换返回值(例如,您经常需要处理 clone())。
2) 请注意,我没有将方法声明为 final,因为可能某些特定的子类会希望使用更适合自己的版本来覆盖它。泛型定义意味着无论谁创建了一个新类或者它是如何定义的,我们仍然可以断言来自例如的返回。 BrandNewSloppilyCodedManipulator.createChild() 仍将是 BrandNewSloppilyCodedManipulator 的一个实例。如果粗心的开发人员试图将其定义为只返回Manipulator,编译器不会让他们这样做。如果他们试图将class 定义为BrandNewSloppilyCodedManipulator<SpellCheckingManipulator>,它也不会让他们这样做。
基本上,结论是,当您想在超类中提供一些在子类中变得更加具体的功能时,这个技巧很有用。通过像这样声明超类,您将锁定任何子类的通用参数成为子类本身。这就是为什么您可以在超类中编写通用的compareTo 或createChild 方法,并防止在处理特定子类时它变得过于模糊。