Por que em java enum é declarado como Enum <E estende Enum >

Duplicar Possível:
java Enum definição

Questão melhor formulada, que não é considerada uma duplicata:
O que seria diferente em Java se a declaração Enum não tivesse a parte recursiva

se os designers de linguagem usassem simplesmente Enum como isso afetaria a linguagem?

A única diferença agora seria que alguém poderia escrever

 A estende Enum 

mas desde que não é permitido em java estender enums que ainda seriam ilegais. Eu também estava pensando em alguém fornecendo jvm um bytecode que define smth como estender um enum – mas os genéricos não podem afetar isso como todos eles são apagados.

Então, qual é o objective dessa declaração?

Obrigado!

Edite para simplificar, vamos olhar um exemplo:

 interface MyComparable  {
     int myCompare (T o);
 }

 class MyEnum  implementa MyComparable  {
     public int myCompare (E o) {retorno -1;  }
 }

 class FirstEnum estende MyEnum  {}

 class SecondEnum estende MyEnum  {}

O que há de errado com essa estrutura de classs? O que pode ser feito que “MyEnum <E estende MyEnum >” restringiria?

Esta é uma pergunta comum, e compreensivelmente. Dê uma olhada nesta parte da FAQ sobre genéricos para a resposta (e, na verdade, leia o máximo possível de todo o documento, pois ele é bem feito e informativo).

A resposta curta é que força a class a ser parametrizada em si mesma; isso é necessário para que as superclasss definam methods, usando o parâmetro genérico, que funcionem de forma transparente (“nativamente”, se você quiser) com suas subclasss.

Edit: Como um exemplo (não) por exemplo, considere o método clone() em Object . Atualmente, é definido para retornar um valor do tipo Object . Graças aos tipos de retorno covariante, subclasss específicas podem (e freqüentemente fazem) definir que retornam uma class mais específica, mas isso não pode ser aplicado e, portanto, não pode ser inferido para uma class arbitrária.

Agora, se Object fosse definido como Enum, ie Object> então você teria que definir todas as classs como algo como public class MyFoo . Conseqüentemente, clone() poderia ser declarado para retornar um tipo de T e você pode assegurar em tempo de compilation que o valor retornado é sempre exatamente a mesma class que o próprio object (nem mesmo as subclasss corresponderiam aos parâmetros).

Agora, neste caso, Object não é parametrizado assim, porque seria extremamente irritante ter essa bagagem em todas as classs, quando 99% deles não vão utilizá-la. Mas, para algumas hierarquias de classs, pode ser muito útil – já usei uma técnica semelhante antes com tipos de analisadores de expressão abstratos e recursivos com várias implementações. Essa construção tornou possível escrever código que era “óbvio” sem ter que converter em todos os lugares, ou copiar e colar apenas para alterar definições de class concretas.

Edite 2 (para realmente responder sua pergunta!):

Se Enum foi definido como Enum , então, como você corretamente diz, alguém poderia definir uma class como A extends Enum . Isso anula o ponto da construção genérica, que é garantir que o parâmetro genérico seja sempre o tipo exato da class em questão. Dando um exemplo concreto, Enum declara seu método compareTo como

 public final int compareTo(E o) 

Neste caso, desde que você definiu A para estender Enum , instâncias de A só poderiam ser comparadas com instâncias de B (qualquer que seja B), o que quase certamente não é muito útil. Com a construção adicional, você sabe que qualquer class que estenda o Enum é comparável apenas contra si mesma. E, portanto, você pode fornecer implementações de methods na superclass que permaneçam úteis e específicas em todas as subclasss.

(Sem este truque genérico recursivo, a única outra opção seria definir compareTo como public final int compareTo(Enum o) . Isso não é realmente a mesma coisa, como então se poderia comparar um java.math.RoundingMode contra um java.lang.Thread.State sem o compilador reclamando, o que novamente não é muito útil.)


OK, vamos nos afastar do próprio Enum , pois parece que estamos ficando presos nele. Em vez disso, aqui está uma class abstrata:

 public abstract class Manipulator> { /** * 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 } } 

Nós vamos ter várias implementações concretas disso – SaveToDatabaseManipulator, SpellCheckingManipulator, o que for. Além disso, também queremos permitir que as pessoas definam as suas próprias, já que esta é uma class super útil. 😉

Agora, você notará que estamos usando a definição genérica recursiva e, em seguida, retornando T do método createChild . Isso significa que:

1) Nós sabemos e o compilador sabe que se eu ligar:

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

então o valor retornado é definitivamente um SpellCheckingManipulator , mesmo que esteja usando a definição da superclass. Os genéricos recursivos aqui permitem que o compilador saiba o que é óbvio para nós, portanto, você não precisa continuar transmitindo os valores de retorno (como, por exemplo, você costuma fazer com clone() ).

2) Observe que não declarei o método final, pois talvez algumas subclasss específicas desejem substituí-lo por uma versão mais adequada para elas mesmas. A definição de genéricos significa que, independentemente de quem criar uma nova class ou como ela é definida, ainda podemos afirmar que o retorno de, por exemplo, BrandNewSloppilyCodedManipulator.createChild() ainda será uma instância do BrandNewSloppilyCodedManipulator . Se um desenvolvedor descuidado tentar defini-lo para retornar apenas o Manipulator , o compilador não permitirá. E se eles tentarem definir a class como BrandNewSloppilyCodedManipulator , também não os deixará.

Basicamente, a conclusão é que esse truque é útil quando você quer fornecer alguma funcionalidade em uma superclass que de alguma forma fica mais específica em subclasss. Ao declarar a superclass como essa, você está bloqueando o parâmetro genérico para que qualquer subclass seja a própria subclass. É por isso que você pode escrever um método genérico compareTo ou createChild na superclass e evitar que ele se torne excessivamente vago quando estiver lidando com subclasss específicas.

A única diferença agora seria que alguém poderia escrever

 A extends Enum 

mas desde que não é permitido em java estender enums que ainda seriam ilegais. Eu também estava pensando em alguém fornecendo jvm um bytecode que define smth como estender um enum – mas os genéricos não podem afetar isso como todos eles são apagados.

Você está certo de que a linguagem impediria alguém de fazer isso. No entanto, o benefício de torná-lo E extends Enum vez de apenas extends Enum é que alguns dos methods no Enum agora retornarão o tipo correto e específico.

O cartaz pediu algo que não funcionaria. Se Enum foi definido como Enum , você poderia fazer isso:

 // Would be legal, because element types of `DaysOfWeek` and `Color` both // extend `Enum`, not `Enum>`. DaysOfWeek d = Enum.valueOf(Color.class, "Purple");