Pensé que entendía bastante bien los genéricos de Java, pero luego encontré lo siguiente en java.lang.Enum:
class Enum<E extends Enum<E>>
¿Alguien podría explicar cómo interpretar este tipo de parámetro? Puntos de bonificación por proporcionar otros ejemplos de dónde se podría usar un parámetro de tipo similar.
Respuestas:
Significa que el argumento de tipo para enum debe derivarse de una enumeración que tiene el mismo argumento de tipo. ¿Cómo puede pasar esto? Al hacer que el argumento de tipo sea el nuevo tipo en sí. Entonces, si tengo una enumeración llamada StatusCode, sería equivalente a:
Ahora, si verifica las restricciones, tenemos
Enum<StatusCode>
, entoncesE=StatusCode
. Vamos a ver: ¿seE
extiendeEnum<StatusCode>
? ¡Si! Estamos bienPuede que se pregunte cuál es el punto de esto :) Bueno, significa que la API de Enum puede referirse a sí misma, por ejemplo, poder decir que se
Enum<E>
implementaComparable<E>
. La clase base puede hacer las comparaciones (en el caso de las enumeraciones) pero puede asegurarse de que solo compara el tipo correcto de enumeraciones entre sí. (EDITAR: Bueno, casi, vea la edición en la parte inferior).He usado algo similar en mi puerto C # de ProtocolBuffers. Hay "mensajes" (inmutables) y "constructores" (mutables, utilizados para construir un mensaje), y vienen en pares de tipos. Las interfaces involucradas son:
Esto significa que de un mensaje puede obtener un generador apropiado (por ejemplo, para tomar una copia de un mensaje y cambiar algunos bits) y de un generador puede obtener un mensaje apropiado cuando haya terminado de construirlo. Sin embargo, es un buen trabajo que los usuarios de la API no necesitan preocuparse realmente por esto: es terriblemente complicado y tomó varias iteraciones para llegar a donde está.
EDITAR: tenga en cuenta que esto no le impide crear tipos extraños que utilizan un argumento de tipo que sí está bien, pero que no es del mismo tipo. El propósito es brindar beneficios en el caso correcto en lugar de protegerlo del caso incorrecto .
Entonces, si de
Enum
todos modos no se manejó "especialmente" en Java, podría (como se señala en los comentarios) crear los siguientes tipos:Second
implementaría enComparable<First>
lugar deComparable<Second>
... pero enFirst
sí estaría bien.fuente
Enum
no tiene ningún método de instancia que devuelva el tipo de parámetro tipo.class Enum<E>
es suficiente en todos los casos. Y en Genéricos, solo debe usar un límite más restrictivo si es realmente necesario para garantizar la seguridad de los tipos.Enum
las subclases no siempre fueron generados automáticamente, la única razón que se necesitaclass Enum<E extends Enum<?>>
másclass Enum<E>
es la capacidad de accederordinal
acompareTo()
. Sin embargo, si lo piensa, no tiene sentido desde el punto de vista del lenguaje permitirle comparar dos tipos diferentes de enumeraciones a través de sus ordinales. Por lo tanto, la implementación deEnum.compareTo()
esos usosordinal
solo tiene sentido en el contexto de lasEnum
subclases que se autogeneran. Si pudiera subclasificar manualmenteEnum
,compareTo
probablemente tendría que serloabstract
.La siguiente es una versión modificada de la explicación del libro de Java Genéricos y Colecciones : Tenemos un
Enum
declararonque se ampliará a una clase
donde
...
será la clase base de alguna manera parametrizada para Enums. Veamos qué tiene que ser eso. Bueno, uno de los requisitosSeason
es que debe implementarseComparable<Season>
. Entonces vamos a necesitar¿Qué podrías usar para
...
permitir que esto funcione? Dado que tiene que ser una parametrizaciónEnum
, la única opción esEnum<Season>
, para que pueda tener:Entonces
Enum
se parametriza en tipos comoSeason
. Resumen deSeason
y obtienes que el parámetro deEnum
es cualquier tipo que satisfagaMaurice Naftalin (coautor, Java Generics and Collections)
fuente
Season
implementeComparable<Season>
?compareTo
método debe haberse declarado como unEnum
subtipo, o el compilador (correctamente) dirá que no tiene un ordinal.Enum
, entonces sería posible tenerloclass OneEnum extends Enum<AnotherEnum>{}
, incluso con la forma en queEnum
se declara en este momento. No tendría mucho sentido para poder comparar un tipo de enumeración con otro, por lo que entoncesEnum
'scompareTo
no tendría sentido según lo declarado todos modos. Los límites no proporcionan ninguna ayuda para esto.public class Enum<E extends Enum<?>>
también sería suficiente.Esto se puede ilustrar con un ejemplo simple y una técnica que se puede utilizar para implementar llamadas a métodos encadenados para subclases. En el siguiente ejemplo, se
setName
devuelve unNode
encadenamiento que no funcionará paraCity
:Entonces podríamos hacer referencia a una subclase en una declaración genérica, de modo que
City
ahora devuelva el tipo correcto:fuente
return (CHILD) this;
considere agregar un método getThis ():protected CHILD getThis() { return this; }
consulte: angelikalanger.com/GenericsFAQ/FAQSections/…Node<T>
no es el caso), los estoy ignorando para ahorrar tiempo.return (SELF) this;
se compila la declaraciónreturn this;
, por lo que podría omitirla .No eres el único que se pregunta qué significa eso; ver el blog caótico de Java .
"Si una clase extiende esta clase, debe pasar un parámetro E. Los límites del parámetro E son para una clase que extiende esta clase con el mismo parámetro E".
fuente
Esta publicación me ha aclarado totalmente este problema de 'tipos genéricos recursivos'. Solo quería agregar otro caso donde esta estructura particular es necesaria.
Supongamos que tiene nodos genéricos en un gráfico genérico:
Entonces puede tener gráficos de tipos especializados:
fuente
class Foo extends Node<City>
donde Foo no está relacionado con la ciudad.class Node<T>
?class Node<T>
es totalmente consistente con tu ejemplo.Si observa el
Enum
código fuente, tiene lo siguiente:Lo primero es lo primero, ¿qué
E extends Enum<E>
significa? Significa que el parámetro de tipo es algo que se extiende desde Enum y no está parametrizado con un tipo sin formato (está parametrizado por sí mismo).Esto es relevante si tienes una enumeración
que, si lo sé correctamente, se traduce a
Esto significa que MyEnum recibe los siguientes métodos:
Y aún más importante,
Esto hace que se
getDeclaringClass()
lance alClass<T>
objeto adecuado .Un ejemplo mucho más claro es el que respondí en esta pregunta donde no puede evitar esta construcción si desea especificar un límite genérico.
fuente
compareTo
ogetDeclaringClass
requiere elextends Enum<E>
límite.Según Wikipedia, este patrón se llama patrón de plantilla curiosamente recurrente . Básicamente, al usar el patrón CRTP, podemos referirnos fácilmente al tipo de subclase sin conversión de tipo, lo que significa que al usar el patrón, podemos imitar la función virtual.
fuente