Je pensais comprendre assez bien les génériques Java, mais je suis ensuite tombé sur ce qui suit dans java.lang.Enum:
class Enum<E extends Enum<E>>
Quelqu'un pourrait-il expliquer comment interpréter ce paramètre de type? Des points bonus pour fournir d'autres exemples d'utilisation d'un paramètre de type similaire.
Réponses:
Cela signifie que l'argument de type pour enum doit dériver d'un enum qui a lui-même le même argument de type. Comment cela peut-il arriver? En faisant de l'argument de type le nouveau type lui-même. Donc, si j'ai une énumération appelée StatusCode, ce serait équivalent à:
Maintenant, si vous vérifiez les contraintes, nous avons
Enum<StatusCode>
- doncE=StatusCode
. Vérifions:E
s'étend-ilEnum<StatusCode>
? Oui! Nous allons bien.Vous vous demandez peut-être à quoi cela sert :) Eh bien, cela signifie que l'API pour Enum peut se référer à elle-même - par exemple, être capable de dire qu'elle
Enum<E>
implémenteComparable<E>
. La classe de base est capable de faire les comparaisons (dans le cas des énumérations) mais elle peut s'assurer qu'elle compare uniquement le bon type d'énumérations entre elles. (EDIT: Eh bien, presque - voir la modification en bas.)J'ai utilisé quelque chose de similaire dans mon port C # de ProtocolBuffers. Il existe des «messages» (immuables) et des «générateurs» (mutables, utilisés pour construire un message) - et ils se présentent sous la forme de paires de types. Les interfaces concernées sont:
Cela signifie qu'à partir d'un message, vous pouvez obtenir un constructeur approprié (par exemple pour prendre une copie d'un message et modifier certains bits) et d'un constructeur, vous pouvez obtenir un message approprié lorsque vous avez fini de le construire. C'est un bon travail que les utilisateurs de l'API n'ont pas besoin de se soucier de cela - c'est horriblement compliqué et il a fallu plusieurs itérations pour en arriver là.
EDIT: Notez que cela ne vous empêche pas de créer des types impairs qui utilisent un argument de type qui lui-même est correct, mais qui n'est pas du même type. Le but est de donner des avantages dans le bon cas plutôt que de vous protéger du mauvais cas.
Donc, si
Enum
vous n'avez pas été manipulé "spécialement" en Java de toute façon, vous pouvez (comme indiqué dans les commentaires) créer les types suivants:Second
mettrait en œuvreComparable<First>
plutôt queComparable<Second>
... maisFirst
lui-même serait très bien.la source
Enum
n'a aucune méthode d'instance qui renvoie le type de paramètre de type.class Enum<E>
c'est suffisant dans tous les cas. Et dans Generics, vous ne devez utiliser une limite plus restrictive que si cela est réellement nécessaire pour garantir la sécurité des types.Enum
sous - classes ne sont pas toujours générées automatiquement, la seule raison pour laquelle vous auriez besoinclass Enum<E extends Enum<?>>
plusclass Enum<E>
est la possibilité d'accéderordinal
àcompareTo()
. Cependant, si vous y réfléchissez, cela n'a pas de sens du point de vue du langage de vous permettre de comparer deux types différents d'énumérations via leurs ordinaux. Par conséquent, l'implémentation deEnum.compareTo()
cette utilisationordinal
n'a de sens que dans le contexte deEnum
sous - classes générées automatiquement. Si vous pouviez sousEnum
- classer manuellement ,compareTo
devrait probablement l'êtreabstract
.Ce qui suit est une version modifiée de l'explication du livre Java Generics and Collections : Nous avons
Enum
déclaréqui sera étendu à une classe
où
...
doit être la classe de base paramétrée pour Enums. Voyons ce que cela doit être. Eh bien, l'une des conditions requisesSeason
est qu'elle doit être mise en œuvreComparable<Season>
. Donc nous allons avoir besoinQue pourriez-vous utiliser pour
...
que cela fonctionne? Étant donné qu'il doit s'agir d'un paramétrage deEnum
, le seul choix estEnum<Season>
, de sorte que vous puissiez avoir:Ainsi
Enum
est paramétré sur des types commeSeason
. Abstrait deSeason
et vous obtenez que le paramètre deEnum
est n'importe quel type qui satisfaitMaurice Naftalin (co-auteur, Java Generics and Collections)
la source
Season
implémentationsComparable<Season>
?compareTo
méthode doit avoir été déclaré comme unEnum
sous-type, sinon le compilateur dira (correctement) qu'il n'a pas d'ordinal.Enum
, alors il serait possible de l'avoirclass OneEnum extends Enum<AnotherEnum>{}
, même avec commentEnum
est déclaré maintenant. Il ne serait pas beaucoup de sens pour être en mesure de comparer un type de ENUM avec un autre, donc alorsEnum
estcompareTo
ne serait pas logique déclarée de toute façon. Les limites n'y apportent aucune aide.public class Enum<E extends Enum<?>>
cela suffirait également.Ceci peut être illustré par un exemple simple et une technique qui peut être utilisée pour implémenter des appels de méthode chaînée pour des sous-classes. Dans un exemple ci - dessous
setName
renvoie unNode
enchaînant donc ne fonctionnera pas pour leCity
:Nous pourrions donc référencer une sous-classe dans une déclaration générique, de sorte que le
City
renvoie maintenant le type correct:la source
return (CHILD) this;
ajouter une méthode getThis ():protected CHILD getThis() { return this; }
Voir: angelikalanger.com/GenericsFAQ/FAQSectionsNode<T>
n'est pas le cas), je les ignore pour gagner du temps.return (SELF) this;
est compiléereturn this;
, vous pouvez donc simplement la laisser de côté.Vous n'êtes pas le seul à vous demander ce que cela signifie; voir le blog Chaotic Java .
«Si une classe étend cette classe, elle doit passer un paramètre E. Les limites du paramètre E sont pour une classe qui étend cette classe avec le même paramètre E».
la source
Cet article m'a totalement clarifié le problème des «types génériques récursifs». Je voulais juste ajouter un autre cas où cette structure particulière est nécessaire.
Supposons que vous ayez des nœuds génériques dans un graphe générique:
Ensuite, vous pouvez avoir des graphiques de types spécialisés:
la source
class Foo extends Node<City>
où Foo n'est pas lié à City.class Node<T>
?class Node<T>
est tout à fait conforme à votre exemple.Si vous regardez le
Enum
code source, il contient les éléments suivants:Tout d'abord, qu'est-ce que
E extends Enum<E>
signifie? Cela signifie que le paramètre type est quelque chose qui s'étend à partir de Enum, et n'est pas paramétré avec un type brut (il est paramétré par lui-même).Ceci est pertinent si vous avez une énumération
qui, si je sais bien, se traduit par
Cela signifie donc que MyEnum reçoit les méthodes suivantes:
Et plus important encore,
Cela rend le
getDeclaringClass()
casting au bonClass<T>
objet .Un exemple bien plus clair est celui auquel j'ai répondu sur cette question où vous ne pouvez pas éviter cette construction si vous souhaitez spécifier une borne générique.
la source
compareTo
ou negetDeclaringClass
nécessite laextends Enum<E>
liaison.Selon wikipedia, ce modèle est appelé modèle de modèle curieusement récurrent . Fondamentalement, en utilisant le modèle CRTP, nous pouvons facilement faire référence au type de sous-classe sans conversion de type, ce qui signifie qu'en utilisant le modèle, nous pouvons imiter la fonction virtuelle.
la source