为什么在 java 枚举中声明为 Enum<E extends Enum<E>>

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3061759/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 00:09:40  来源:igfitidea点击:

Why in java enum is declared as Enum<E extends Enum<E>>

javaenums

提问by atamur

Possible Duplicate:
java Enum definition

可能的重复:
java Enum 定义

Better formulated question, that is not considered a duplicate:
What would be different in Java if Enum declaration didn't have the recursive part

更好地表述的问题,这不被认为是重复的:
如果 Enum 声明没有递归部分,Java 会有什么不同

if language designers were to use simply Enum<E extends Enum> how would that affect the language?

如果语言设计者只使用 Enum<E extends Enum> 会如何影响语言?

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.

但是因为在java中不允许扩展仍然是非法的枚举。我也在考虑有人向 jvm 提供一个字节码,该字节码将 smth 定义为扩展枚举 - 但泛型不能影响它,因为它们都被删除了。

So what is the whole point of such declaration?

那么这种声明的全部意义是什么?

Thank you!

谢谢!

Editfor 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?

这个类结构有什么问题?“MyEnum<E extends MyEnum<E>>”会限制什么?

回答by Andrzej Doyle

This is a common question, and understandably so. Have a look at this part of the generics FAQfor 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 on Object. Currently, it's defined to return a value of type Object. 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.

编辑:作为(非)例如用于例如,考虑clone()对方法Object。目前,它被定义为返回一个类型的值Object。由于协变返回类型,特定的子类可以(并且经常这样做)定义它们返回一个更特定的类,但这不能强制执行,因此不能为任意类推断出。

Now, ifObject were defined like Enum, i.e. Object<T extends Object<T>>then you'd have to define all classes as something like public class MyFoo<MyFoo>. Consequently, clone()could be declared to return a type of Tand 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).

现在,如果Object 像 Enum 一样定义,即,Object<T extends Object<T>>那么您必须将所有类定义为类似public class MyFoo<MyFoo>. 因此,clone()可以声明为返回类型,T并且您可以在编译时确保返回的值始终与对象本身完全相同(甚至子类都不匹配参数)。

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.

现在在这种情况下,Object 没有像这样参数化,因为当 99% 的类根本不会使用它时,在所有类上都有这个包袱会非常烦人。但是对于某些类层次结构,它可能非常有用 - 我自己之前使用过类似的技术,处理具有多种实现的抽象递归表达式解析器类型。这种构造使得编写“显而易见”的代码成为可能,而不必到处进行强制转换,或者只是为了更改具体的类定义而进行复制和粘贴。

Edit 2(To actually answer your question!):

编辑 2(实际回答您的问题!):

If Enum was defined as Enum<E extends Enum>, then as you rightly say, someone could define a class as A extends Enum<B>. This defeats the point of the generic construct, which is to ensure that the generic parameter is always the exacttype of the class in question. Giving a concrete example, Enum declares its compareTo method as

如果 Enum 被定义为Enum<E extends Enum>,那么正如您所说的那样,有人可以将类定义为A extends Enum<B>。这违背了泛型构造的要点,即确保泛型参数始终是相关类的确切类型。举一个具体的例子,Enum 将它的 compareTo 方法声明为

public final int compareTo(E o)

In this case, since you defined Ato extend Enum<B>, instances of Acould only be compared against instances of B(whatever B is), which is almost certainly not very useful. Withthe additional construct, you know that anyclass 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.

在这种情况下,由于您定义A为 extend Enum<B>A因此只能将 of 的实例与B(无论 B 是什么)的实例进行比较,这几乎肯定不是很有用。 通过附加构造,您知道任何扩展 Enum 的类都只能与它自己进行比较。因此,您可以在超类中提供在所有子类中仍然有用且特定的方法实现。

(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 a java.math.RoundingModeagainst a java.lang.Thread.Statewithout the compiler complaining, which again isn't very useful.)

(如果没有这个递归泛型技巧,唯一的其他选择就是将 compareTo 定义为 as public final int compareTo(Enum o)。这不是一回事,因为这样就可以将 ajava.math.RoundingMode与 a进行比较java.lang.Thread.State而编译器不会抱怨,这又不是很有用。)



OK, let's get away from Enumitself as we appear to be getting hung up on it. Instead, here is an abstract class:

好吧,让我们远离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
    }
}

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. ;-)

我们将有几个具体的实现——SaveToDatabaseManipulator、SpellCheckingManipulator 等等。此外,我们还想让人们定义自己的类,因为这是一个非常有用的类。;-)

Now - you will notice that we're using the recursive generic definition, and then returning Tfrom the createChildmethod. This means that:

现在 - 您会注意到我们正在使用递归泛型定义,然后T从该createChild方法返回。这意味着:

1) We know and the compiler knowsthat if I call:

1)我们知道并且编译器知道如果我调用:

SpellCheckingManipulator obj = ...; // We have a reference somehow
return obj.createChild();

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 with clone(), for example).

那么返回的值肯定是 a SpellCheckingManipulator,即使它使用的是超类的定义。这里的递归泛型允许编译器知道什么对我们来说是显而易见的,所以你不必继续转换返回值(就像你经常需要做的那样clone(),例如)。

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 of BrandNewSloppilyCodedManipulator. If a careless developer tries to define it to return just Manipulator, the compiler won't let them. And if they try to define the classas BrandNewSloppilyCodedManipulator<SpellCheckingManipulator>, it won't let them either.

2) 请注意,我没有将方法声明为 final,因为也许某些特定的子类想要用更适合自己的版本来覆盖它。泛型定义意味着不管谁创建了一个新类或它是如何定义的,我们仍然可以断言 eg 的返回BrandNewSloppilyCodedManipulator.createChild()仍然是 的实例BrandNewSloppilyCodedManipulator。如果粗心的开发人员试图将其定义为仅返回Manipulator,则编译器不会让他们这样做。如果他们试图将定义为BrandNewSloppilyCodedManipulator<SpellCheckingManipulator>,它也不会让他们这样做。

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 downthe generic parameter for any subclasses to be the subclass itself. This is why you can write a generic compareToor createChildmethod in the superclass and prevent it from becoming overly vague when you're dealing with specific subclasses.

基本上,结论是当您想在超类中提供一些在子类中变得更具体的功能时,这个技巧很有用。通过像这样声明超类,您将任何子类的泛型参数锁定为子类本身。这就是为什么您可以在超类中编写泛型compareTocreateChild方法,并防止在处理特定子类时它变得过于模糊。