可能的重复:
java 枚举定义
更好地表述问题,不被视为重复:
会有什么不同在Java中如果Enum声明没有递归部分
如果语言设计者简单地使用 Enum 的话。这会对语言产生什么影响?
现在唯一的区别是有人可以写
A extends Enum<B>
,但由于java中不允许扩展枚举,这仍然是非法的。
我也在考虑有人向 jvm 提供一个字节码,将 smth 定义为扩展枚举 - 但泛型不会影响它,因为它们都被删除了。
那么这样的声明的全部意义是什么?
谢谢你!
编辑
为了简单起见,让我们看一个例子:
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>”可以做什么会限制?
Possible Duplicate:
java Enum definition
Better formulated question, that is not considered a duplicate:
What would be different in Java if Enum declaration didn't have the recursive part
if language designers were to use simply Enum<E extends Enum> how would that affect the language?
The only difference now would be that someone coud write
A extends Enum<B>
but since it is not allowed in java to extend enums that would be still illegal.
I was also thinking about someone supplying jvm a bytecode that defines smth as extending an enum - but generics can't affect that as they all are erased.
So what is the whole point of such declaration?
Thank you!
Edit
for simplicity let's look at an example:
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> {}
what's wrong with this class structure? What can be done that "MyEnum<E extends MyEnum<E>>" would restrict?
发布评论
评论(1)
这是一个常见的问题,也是可以理解的。请查看这部分仿制药常见问题解答以获得答案(以及实际上,请尽可能多地阅读整个文档,它做得相当好,内容丰富)。
简而言之,它强制类对其自身进行参数化;这是超类使用通用参数定义方法所必需的,这些方法与其子类透明地(“本机”,如果您愿意的话)一起工作。
编辑:作为一个(非)示例,请考虑
Object
上的clone()
方法。目前,它被定义为返回Object
类型的值。由于协变返回类型,特定的子类可以(并且经常)定义它们返回更特定的类,但这不能强制执行,因此不能推断任意类。现在,如果 Object 的定义类似于 Enum,即
Object>
那么您必须将所有类定义为public类 MyFoo
。因此,clone()
可以声明为返回T
类型,并且您可以在编译时确保返回值始终与对象本身完全相同的类(甚至子类也不匹配参数)。现在,在这种情况下,Object 不会像这样参数化,因为当 99% 的类根本不会使用它时,在所有类上都带有这个包袱会非常烦人。但对于某些类层次结构来说,它可能非常有用 - 我之前自己也使用过类似的技术,用于具有多种实现的抽象递归表达式解析器类型。这种构造使得编写“显而易见”的代码成为可能,而不必到处进行强制转换,或者复制粘贴只是为了更改具体的类定义。
编辑2(真正回答你的问题!):
如果 Enum 被定义为
Enum
,那么正如你所说,有人可以将类定义为 <代码>A扩展了Enum。这违背了泛型构造的要点,即确保泛型参数始终是相关类的精确类型。举一个具体的例子,Enum 声明其compareTo方法为在这种情况下,由于您定义了
A
来扩展Enum
,所以A
的实例只能与 B 的实例(无论 B 是什么)进行比较,这几乎肯定不是很有用。 使用附加构造,您知道任何扩展 Enum 的类只能与其自身进行比较。因此,您可以在超类中提供在所有子类中仍然有用且特定的方法实现。(如果没有这种递归泛型技巧,唯一的其他选择是将compareTo定义为
public final intcompareTo(Enum o)
。这实际上并不是同一件事,因为这样就可以比较一个java.math.RoundingMode
针对java.lang.Thread.State
而没有编译器抱怨,这又不是很有用。)好吧,让我们远离
Enum< /code> 本身,因为我们似乎对此很着迷。相反,这里是一个抽象类:
我们将有几个具体的实现 - SaveToDatabaseManipulator、SpellCheckingManipulator 等等。此外,我们还想让人们定义自己的类,因为这是一个超级有用的类。 ;-)
现在 - 您会注意到我们正在使用递归泛型定义,然后从
createChild
方法返回T
。这意味着:1)我们知道并且编译器知道如果我调用:
那么返回的值肯定是一个
SpellCheckingManipulator
,即使它使用的是超类的定义。这里的递归泛型允许编译器知道什么对我们来说是显而易见的,因此您不必继续转换返回值(例如,就像您经常需要使用clone()
所做的那样)。2)请注意,我没有声明该方法为final,因为也许某些特定的子类想要用更适合自己的版本来重写它。泛型定义意味着无论谁创建新类或如何定义它,我们仍然可以断言从例如 BrandNewSloppilyCodedManipulator.createChild() 的返回仍将是 BrandNewSloppilyCodedManipulator
的实例/代码>。如果粗心的开发人员尝试将其定义为仅返回
Manipulator
,编译器将不会允许他们这样做。如果他们尝试将类定义为BrandNewSloppilyCodedManipulator
,它也不会允许他们这样做。基本上,结论是,当您想在超类中提供某些功能而在子类中以某种方式变得更加具体时,此技巧非常有用。通过这样声明超类,您可以将任何子类的通用参数锁定为子类本身。这就是为什么您可以在超类中编写通用的
compareTo
或createChild
方法,并防止它在处理特定子类时变得过于模糊。This is a common question, and understandably so. Have a look at this part of the generics FAQ for the answer (and actually, read as much of the whole document as you feel comfortable with, it's rather well done and informative).
The short answer is that it forces the class to be parameterized on itself; this is required for superclasses to define methods, using the generic parameter, that work transparently ("natively", if you will) with their subclasses.
Edit: As a (non-)example for instance, consider the
clone()
method onObject
. Currently, it's defined to return a value of typeObject
. Thanks to covariant return types, specific subclasses can (and often do) define that they return a more specific class, but this cannot be enforced and hence cannot be inferred for an arbitrary class.Now, if Object were defined like Enum, i.e.
Object<T extends Object<T>>
then you'd have to define all classes as something likepublic class MyFoo<MyFoo>
. Consequently,clone()
could be declared to return a type ofT
and you can ensure at compile time that the returned value is always exactly the same class as the object itself (not even subclasses would match the parameters).Now in this case, Object isn't parameterized like this because it would be extremely annoying to have this baggage on all classes when 99% of them aren't going to utilise it at all. But for some class hierarchies it can be very useful - I've used a similar technique myself before with types of abstract, recursive expression parsers with several implementations. This construct made it possible to write code that was "obvious" without having to cast everywhere, or copy-and-paste just to change concrete class definitions.
Edit 2 (To actually answer your question!):
If Enum was defined as
Enum<E extends Enum>
, then as you rightly say, someone could define a class asA extends Enum<B>
. This defeats the point of the generic construct, which is to ensure that the generic parameter is always the exact type of the class in question. Giving a concrete example, Enum declares its compareTo method asIn this case, since you defined
A
to extendEnum<B>
, instances ofA
could only be compared against instances ofB
(whatever B is), which is almost certainly not very useful. With the additional construct, you know that any class that extends Enum is comparable only against itself. And hence you can provide method implementations in the superclass that remain useful, and specific, in all subclasses.(Without this recursive generics trick, the only other option would be to define compareTo as
public final int compareTo(Enum o)
. This is not really the same thing, as then one could compare ajava.math.RoundingMode
against ajava.lang.Thread.State
without the compiler complaining, which again isn't very useful.)OK, let's get away from
Enum
itself as we appear to be getting hung up on it. Instead, here is an abstract class:We are going to have several concrete implementations of this - SaveToDatabaseManipulator, SpellCheckingManipulator, whatever. Additionally we also want to let people define their own, as this is a super-useful class. ;-)
Now - you will notice that we're using the recursive generic definition, and then returning
T
from thecreateChild
method. This means that:1) We know and the compiler knows that if I call:
then the returned value is definitely a
SpellCheckingManipulator
, even though it's using the definition from the superclass. The recursive generics here allow the compiler to know what is obvious to us, so you don't have to keep casting the return values (like you often have to do withclone()
, for example).2) Notice that I didn't declare the method final, since perhaps some specific subclasses will want to override it with a more suitable version for themselves. The generics definition means that regardless of who create a new class or how it is defined, we can still assert that the return from e.g.
BrandNewSloppilyCodedManipulator.createChild()
will still be an instance ofBrandNewSloppilyCodedManipulator
. If a careless developer tries to define it to return justManipulator
, the compiler won't let them. And if they try to define the class asBrandNewSloppilyCodedManipulator<SpellCheckingManipulator>
, it won't let them either.Basically, the conclusion is that this trick is useful when you want to provide some functionality in a superclass that somehow gets more specific in subclasses. By declaring the superclass like this, you are locking down the generic parameter for any subclasses to be the subclass itself. This is why you can write a generic
compareTo
orcreateChild
method in the superclass and prevent it from becoming overly vague when you're dealing with specific subclasses.