这是一个常见的问题,也是可以理解的。看一下这部分是仿制药常见问题解答寻找答案(实际上,请尽可能多地阅读整个文档,它做得相当好,内容丰富)。
简而言之,它强制类对其自身进行参数化;这是超类使用通用参数定义方法所必需的,这些方法与其子类透明地(“本机”,如果您愿意的话)一起工作。
编辑:作为一个(非)示例,请考虑clone()
方法上Object
。目前,它被定义为返回类型的值Object
。由于协变返回类型,特定的子类可以(并且通常确实)定义它们返回更特定的类,但这不能强制执行,因此不能推断任意类。
Now, if对象的定义类似于枚举,即Object<T extends Object<T>>
那么你必须将所有类定义为类似的public class MyFoo<MyFoo>
。最后,clone()
可以声明返回一个类型T
并且您可以在编译时确保返回的值始终与对象本身完全相同(甚至子类也不会与参数匹配)。
现在,在这种情况下,Object 不会像这样参数化,因为当 99% 的类根本不会使用它时,在所有类上都带有这个包袱会非常烦人。但对于某些类层次结构来说,它可能非常有用 - 我之前自己也使用过类似的技术,用于具有多种实现的抽象递归表达式解析器类型。这种构造使得编写“显而易见”的代码成为可能,而不必到处进行强制转换,或者复制粘贴只是为了更改具体的类定义。
Edit 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 是什么),这几乎肯定不是很有用。With额外的构造,你知道any扩展 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 等等。此外,我们还想让人们定义自己的类,因为这是一个超级有用的类。 ;-)
现在 - 您会注意到我们正在使用递归通用定义,然后返回T
来自createChild
方法。这意味着:
1)我们知道并且编译器知道如果我打电话:
SpellCheckingManipulator obj = ...; // We have a reference somehow
return obj.createChild();
那么返回值肯定是SpellCheckingManipulator
,即使它使用的是超类的定义。这里的递归泛型允许编译器知道什么对我们来说是显而易见的,因此您不必继续转换返回值(就像您经常需要做的那样)clone()
, 例如)。
2)请注意,我没有声明该方法为final,因为也许某些特定的子类想要用更适合自己的版本来重写它。泛型定义意味着无论谁创建了一个新类或如何定义它,我们仍然可以断言来自例如的返回BrandNewSloppilyCodedManipulator.createChild()
仍将是一个实例BrandNewSloppilyCodedManipulator
。如果粗心的开发人员试图将其定义为仅返回Manipulator
,编译器不会允许它们。如果他们试图定义class as BrandNewSloppilyCodedManipulator<SpellCheckingManipulator>
,它也不会让他们。
基本上,结论是,当您想在超类中提供某些功能并且在子类中以某种方式变得更加具体时,此技巧非常有用。通过这样声明超类,您可以锁定任何子类的通用参数都是子类本身。这就是为什么你可以写一个通用的compareTo
or createChild
超类中的方法,并防止在处理特定子类时它变得过于模糊。