Pensei entender bem os genéricos Java, mas me deparei com o seguinte em java.lang.Enum:
class Enum<E extends Enum<E>>
Alguém poderia explicar como interpretar esse parâmetro de tipo? Pontos de bônus por fornecer outros exemplos de onde um parâmetro de tipo semelhante pode ser usado.
Respostas:
Isso significa que o argumento de tipo para enum deve derivar de um enum que possui o mesmo argumento de tipo. Como isso pode acontecer? Tornando o argumento de tipo o novo tipo em si. Portanto, se eu tenho uma enumeração chamada StatusCode, seria equivalente a:
Agora, se você verificar as restrições, temos
Enum<StatusCode>
- entãoE=StatusCode
. Vamos verificar: seE
estendeEnum<StatusCode>
? Sim! Nós estamos bem.Você pode estar se perguntando qual é o sentido disso :) Bem, isso significa que a API do Enum pode se referir a si mesma - por exemplo, ser capaz de dizer que
Enum<E>
implementaComparable<E>
. A classe base é capaz de fazer as comparações (no caso de enumerações), mas pode garantir que apenas compare o tipo certo de enumeração entre si. (EDIT: Bem, quase - veja a edição na parte inferior.)Eu usei algo semelhante na minha porta C # do ProtocolBuffers. Existem "mensagens" (imutáveis) e "construtores" (mutáveis, usadas para criar uma mensagem) - e elas vêm como pares de tipos. As interfaces envolvidas são:
Isso significa que, a partir de uma mensagem, você pode obter um construtor apropriado (por exemplo, para tirar uma cópia de uma mensagem e alterar alguns bits) e, a partir de um construtor, você pode receber uma mensagem apropriada quando terminar de construí-la. É um bom trabalho que os usuários da API não precisem realmente se preocupar com isso - é terrivelmente complicado e levou várias iterações para chegar onde está.
Edição: Observe que isso não impede você de criar tipos ímpares que usam um argumento de tipo que por si só é bom, mas que não é do mesmo tipo. O objetivo é oferecer benefícios no caso certo , em vez de protegê-lo do caso errado .
Portanto, se
Enum
não fosse tratado "especialmente" em Java, você poderia (como observado nos comentários) criar os seguintes tipos:Second
implementaria emComparable<First>
vez deComparable<Second>
... mas emFirst
si seria bom.fonte
Enum
não possui nenhum método de instância que retorne o tipo de parâmetro typeclass Enum<E>
é suficiente em todos os casos. E em genéricos, você só deve usar um limite mais restritivo se for realmente necessário para garantir a segurança do tipo.Enum
subclasses nem sempre foram gerada automaticamente, a única razão que você precisaria declass Enum<E extends Enum<?>>
maisclass Enum<E>
é a capacidade de acessoordinal
paracompareTo()
. No entanto, se você pensar sobre isso, não faz sentido do ponto de vista do idioma permitir comparar dois tipos diferentes de enumerações por meio de seus ordinais. Portanto, a implementação dessesEnum.compareTo()
usosordinal
só faz sentido no contexto deEnum
subclasses geradas automaticamente. Se você pudesse subclasses manualmenteEnum
,compareTo
provavelmente teria que serabstract
.A seguir, uma versão modificada da explicação do livro Java Generics and Collections : Temos uma
Enum
declaraçãoque será expandido para uma classe
onde
...
deve ser a classe base de alguma forma parametrizada para Enums. Vamos descobrir o que isso tem que ser. Bem, um dos requisitos paraSeason
isso é que ele deve ser implementadoComparable<Season>
. Então, vamos precisarO que você poderia usar para
...
permitir que isso funcionasse? Dado que deve ser uma parametrizaçãoEnum
, a única opção éEnum<Season>
, para que você possa ter:Então,
Enum
é parametrizado em tipos comoSeason
. Resumo deSeason
e você obtém que o parâmetro deEnum
é qualquer tipo que satisfaçaMaurice Naftalin (co-autor, Java Generics and Collections)
fonte
Season
implementaComparable<Season>
?compareTo
método deve ter sido declarado como umEnum
subtipo, ou o compilador (corretamente) dirá que não possui um ordinal.Enum
, seria possívelclass OneEnum extends Enum<AnotherEnum>{}
, mesmo com o queEnum
é declarado agora. Não faria muito sentido para ser capaz de comparar um tipo de enumeração com o outro, por isso, em seguida,Enum
'scompareTo
não faria sentido, conforme declarado de qualquer maneira. Os limites não fornecem nenhuma ajuda para isso.public class Enum<E extends Enum<?>>
também seria suficiente.Isso pode ser ilustrado por um exemplo simples e uma técnica que pode ser usada para implementar chamadas de método em cadeia para subclasses. Em um exemplo abaixo,
setName
umNode
encadeamento não funcionará paraCity
:Assim, poderíamos referenciar uma subclasse em uma declaração genérica, para que
City
agora retorne o tipo correto:fonte
return (CHILD) this;
considere adicionar um método getThis ():protected CHILD getThis() { return this; }
Consulte: angelikalanger.com/GenericsFAQ/FAQSections/…Node<T>
não é o caso), eu os estou ignorando para economizar tempo.return (SELF) this;
é compiladareturn this;
, para que você possa deixar de fora.Você não é o único se perguntando o que isso significa; consulte o blog Java caótico .
“Se uma classe estender essa classe, deve passar o parâmetro E. Os limites do parâmetro E são para uma classe que estende essa classe com o mesmo parâmetro E”.
fonte
Este post me esclareceu totalmente esse problema de 'tipos genéricos recursivos'. Eu só queria adicionar outro caso em que essa estrutura específica seja necessária.
Suponha que você tenha nós genéricos em um gráfico genérico:
Então você pode ter gráficos de tipos especializados:
fonte
class Foo extends Node<City>
onde Foo não tem relação com a City.class Node<T>
?class Node<T>
é totalmente consistente com o seu exemplo.Se você olhar para o
Enum
código-fonte, ele possui o seguinte:Primeira coisa, o que
E extends Enum<E>
significa? Isso significa que o parâmetro type é algo que se estende do Enum e não é parametrizado com um tipo bruto (ele é parametrizado por si só).Isso é relevante se você tiver um enum
que, se eu sei corretamente, é traduzido para
Portanto, isso significa que o MyEnum recebe os seguintes métodos:
E ainda mais importante,
Isso faz o
getDeclaringClass()
lançamento para oClass<T>
objeto apropriado .Um exemplo muito mais claro é o que eu respondi nesta pergunta em que você não pode evitar essa construção se desejar especificar um limite genérico.
fonte
compareTo
ougetDeclaringClass
exige oextends Enum<E>
limite.Segundo a wikipedia, esse padrão é chamado de padrão de modelo curiosamente recorrente . Basicamente, usando o padrão CRTP, podemos facilmente nos referir ao tipo de subclasse sem conversão de tipo, o que significa que, usando o padrão, podemos imitar a função virtual.
fonte