Une raison pour cela consiste à tester la situation où il existe une valeur d'énumération non valide sans introduire une valeur d'énumération non valide dans la source principale.
Archimedes Trajano
Oui un exemple de pureté "linguistique". Je pense que ce qui est souhaité est pour l'idée d'économie de main-d'œuvre "d'un ensemble incrémenté automatique d'entiers comme celui que l'on a en C ++ afin que vous puissiez démarrer un nouvel ensemble comme une extension de l'ancien ensemble commençant à 1+ la dernière valeur de la ensemble précédent, et si les entrées sont nommées, héritez les noms du "sous-ensemble commun". Bien que l'énumération java ait de bonnes choses à ce sujet, il lui manque l'aide de déclaration d'entier à incrémentation automatique simple fournie par l'énumération C ++.
peterk
4
En fait, lorsque vous étendez votre énumération avec de nouvelles valeurs, vous créez non pas une sous-classe, mais une superclasse. Vous pouvez utiliser des valeurs d'énumération de base partout au lieu de l'énumération "étendue", mais pas l'inverse, donc selon le principe de substitution de Liskov, l'énumération étendue est une superclasse de l'énumération de base.
Ilya
@Ilya ... oui c'est vrai. Je souligne que la question a des cas d'utilisation précis dans le monde réel. Par souci d'arguments, envisager une base de ENUM: PrimaryColours; il est raisonnable de vouloir super -class cela Enum PrimaryAndPastelColoursen ajoutant de nouveaux noms de couleurs. Liskov est toujours l'éléphant dans la pièce. Alors pourquoi ne pas commencer par une énumération de base de: AllMyColours- Et puis on pourrait sous- classer toutes les couleurs à: PrimaryAndPastelColourset ensuite sous- classer ceci à: PrimaryColours(en gardant la hiérarchie à l'esprit). Java ne le permettra pas non plus.
le
Réponses:
451
Non, vous ne pouvez pas faire cela en Java. En dehors de toute autre chose, dserait alors vraisemblablement une instance de A(étant donné l'idée normale de "s'étend"), mais les utilisateurs qui ne connaissaient que Ane le sauraient pas - ce qui va à l'encontre de l'énumération étant un ensemble bien connu de valeurs.
Si vous pouviez nous en dire plus sur la façon dont vous souhaitez l' utiliser , nous pourrions potentiellement suggérer des solutions alternatives.
Toutes les énumérations étendent implicitement java.lang.Enum. Comme Java ne prend pas en charge l'héritage multiple, une énumération ne peut pas étendre autre chose.
givanse
9
La raison pour laquelle je veux étendre est parce que j'aimerais avoir une classe de base appelée par exemple IntEnum, qui ressemble à ceci: stackoverflow.com/questions/1681976/enum-with-int-value-in-java/… . Ensuite, toutes mes énumérations pourraient l'étendre ... dans ce cas, bénéficiant simplement de l'héritage et donc je n'aurais pas à dupliquer fréquemment ce code "énumération basée sur l'intégralité". Je suis nouveau sur Java et je viens de C #, et j'espère qu'il me manque quelque chose. Mon opinion actuelle est que les énumérations Java sont une douleur par rapport à C #.
Tyler Collier
30
@Tyler: les énumérations C # ne sont que des noms associés à des nombres, sans validation automatique ou quoi que ce soit . Les énumérations IMO sont le seul bit de Java qui est en fait meilleur que C #.
Jon Skeet
21
Pas d'accord avec @JonSkeet ici. Dans mon cas d'utilisation, je voudrais séparer toute la logique désagréable de mon grand énumération, avoir la logique cachée et définir une énumération propre qui étend l'autre qui est cachée. Des énumérations avec beaucoup de logique battent l'idée d'avoir des variables propres déclarées afin que vous n'ayez pas à déclarer des centaines de variables de chaînes statiques, donc une classe avec 5 énumérations ne devient pas illisible et trop grande en lignes. Je ne veux pas non plus que les autres développeurs se préoccupent de copier et de coller cette paix de code pour le prochain projet et d'étendre plutôt le base_enum ... cela a du sens pour moi ...
mmm
43
@givanse ... n'est pas d'accord avec vous sur le point de l'extension implicite de java.lang.Enum étant la cause du non-héritage car chaque classe en java hérite également implicitement de la classe Object mais elle peut hériter d'une autre classe comme elle viendrait ensuite dans la hiérarchie comme Object->A->Bau lieu deObject->A->B extends Object
mickeymoon
317
Les énumérations représentent une énumération complète des valeurs possibles. La réponse (inutile) est donc non.
Comme exemple de problème réel, prenez les jours de la semaine, les jours de week-end et, le syndicat, les jours de semaine. Nous pourrions définir tous les jours dans les jours de la semaine, mais nous ne serions pas en mesure de représenter des propriétés spéciales pour les jours de la semaine et les week-ends.
Ce que nous pourrions faire, c'est avoir trois types d'énumération avec une correspondance entre les jours de la semaine / week-end et les jours de la semaine.
N'y a-t-il pas un problème avec ça? Une instruction switch ne fonctionnera pas sur une interface, mais elle fonctionne sur une énumération régulière. Ne pas fonctionner avec un commutateur tue une des choses les plus agréables des énumérations.
Manius
9
Je pense qu'il pourrait y avoir un autre problème avec cela. Il n'y a pas d'égalité entre Weekday.MON et DayOfWeek.MON. N'est-ce pas là l'autre gros avantage des énumérations? Je n'ai pas de meilleure solution, je m'en rends compte alors que j'essaie de trouver la meilleure réponse. Le fait de ne pas pouvoir utiliser == force un peu la main.
Snekse
2
@Crusader oui, c'est précisément le compromis. Si vous voulez quelque chose d'extensible, vous ne pouvez pas avoir des instructions de commutation fixes, si vous voulez un ensemble de valeurs connues fixes, vous ne pouvez pas tautologiquement avoir quelque chose d'extensible.
djechlin
3
En passant de l'énumération à l'interface, vous perdez également l'appel statique à values (). Cela rend la refactorisation difficile, surtout si vous décidez d'étendre votre énumération et d'ajouter l'interface en tant que barrière d'abstraction à une énumération établie.
Joshua Goldberg
4
Cette approche consistant à dériver une énumération à partir d'une interface est utilisée par l'API Java 1.7, par exemple java.nio.file.Files.write () prend un tableau d'OpenOption comme dernier argument. OpenOption est une interface, mais lorsque nous appelons cette fonction, nous passons généralement une constante d'énumération StandardOpenOption, qui est dérivée d'OpenOption. Cela a l'avantage d'être extensible, mais il présente également des inconvénients. L'implémentation souffre du fait qu'OpenOption est une interface. Il crée un HashSet <OpenOption> à partir du tableau passé, alors qu'il aurait pu créer un EnumSet plus efficace en termes d'espace et de temps. Et il ne peut pas utiliser de commutateur.
Cela implique la création d'une interface et son utilisation là où vous utilisez actuellement l'énumération. Ensuite, faites enum implémenter l'interface. Vous pouvez ajouter plus de constantes en faisant en sorte que cette nouvelle énumération étende également l'interface.
Cela vaut la peine d'appeler leur utilisation d'une méthode d'usine dans l'interface. Excellent moyen de partager des fonctionnalités communes entre des énumérations liées, car l'extension n'est pas une solution viable.
Tim Clemons
8
Pouvez-vous fournir plus de détails (code :)) sur ce modèle?
Dherik
3
Ce modèle ne permet pas d'étendre les valeurs d'une énumération. Quel est le point de la question posée.
Eria
55
Sous les couvertures, votre ENUM n'est qu'une classe régulière générée par le compilateur. Cette classe générée s'étend java.lang.Enum. La raison technique pour laquelle vous ne pouvez pas étendre la classe générée est que la classe générée l'est final. Les raisons conceptuelles pour lesquelles il est définitif sont discutées dans cette rubrique. Mais je vais ajouter la mécanique à la discussion.
Voici une énumération de tests:
publicenum TEST {
ONE, TWO, THREE;}
Le code résultant de javap:
publicfinalclass TEST extends java.lang.Enum<TEST>{publicstaticfinal TEST ONE;publicstaticfinal TEST TWO;publicstaticfinal TEST THREE;static{};publicstatic TEST[] values();publicstatic TEST valueOf(java.lang.String);}
En théorie, vous pouvez taper cette classe par vous-même et supprimer la "finale". Mais le compilateur vous empêche d'étendre directement "java.lang.Enum". Vous pourriez décider de NE PAS étendre java.lang.Enum, mais votre classe et ses classes dérivées ne seraient pas une instance de java.lang.Enum ... ce qui pourrait ne pas vraiment vous intéresser!
Il ne contient aucun code. Le programme "javap" montre le bloc vide.
ChrisCantrell
C'est étrange de l'avoir là-bas s'il ne fait rien, n'est-ce pas?
soote
4
Tu as raison! Mon erreur. Ce n'est PAS un bloc de code vide. Si vous exécutez "javap -c", vous verrez le code réel à l'intérieur du bloc statique. Le bloc statique crée toutes les instances ENUM (UN, DEUX et TROIS ici). Désolé pour ça.
ChrisCantrell
1
Merci d'avoir déclaré le fait: parce que java.lang.Enum est déclaré final.
Benjamin
26
enum A {a,b,c}enum B extends A {d}/*B is {a,b,c,d}*/
peut s'écrire:
publicenumAll{
a (ClassGroup.A,ClassGroup.B),
b (ClassGroup.A,ClassGroup.B),
c (ClassGroup.A,ClassGroup.B),
d (ClassGroup.B)...
ClassGroup.B.getMembers () contient {a, b, c, d}
Comment cela peut être utile: Disons que nous voulons quelque chose comme: Nous avons des événements et nous utilisons des énumérations. Ces énumérations peuvent être regroupées par un traitement similaire. Si nous avons une opération avec de nombreux éléments, certains événements démarrent l'opération, certains ne sont qu'une étape et d'autres mettent fin à l'opération. Pour rassembler une telle opération et éviter un long commutateur, nous pouvons les regrouper comme dans l'exemple et utiliser:
Au-dessus si nous avons un échec (myEvent.is (State_StatusGroup.FAIL)) puis en itérant par les événements précédents, nous pouvons facilement vérifier si nous devons annuler le transfert d'argent en:
intéressant, nous pourrions également utiliser le très énumération toString (), et à la fin comparer les chaînes; et pour utiliser switch, il suffirait de convertir l'objet en une énumération connue; le seul problème serait que 2 développeurs étendent et créent un identifiant d'énumération identique, puis essaient de fusionner les deux codes :), je pense maintenant comprendre pourquoi l'énumération doit rester non extensible.
Aquarius Power
11
Au cas où vous l'auriez manqué, il y a un chapitre dans l'excellent livre de Joshua Bloch " Java Effective, 2nd edition ".
Chapitre 6 - Énumérations et annotations
Point 34: émuler des énumérations extensibles avec des interfaces
Un inconvénient mineur de l'utilisation d'interfaces pour émuler des énumérations extensibles est que les implémentations ne peuvent pas être héritées d'un type d'énumération à un autre. Dans le cas de notre exemple d'opération, la logique de stockage et de récupération du symbole associé à une opération est dupliquée dans BasicOperation et ExtendedOperation. Dans ce cas, cela n'a pas d'importance car très peu de code est dupliqué. S'il y avait une plus grande quantité de fonctionnalités partagées, vous pouvez l'encapsuler dans une classe d'assistance ou une méthode d'assistance statique pour éliminer la duplication de code.
En résumé, bien que vous ne puissiez pas écrire un type d'énumération extensible, vous pouvez l'émuler en écrivant une interface pour aller avec un type d'énumération de base qui implémente l'interface. Cela permet aux clients d'écrire leurs propres énumérations qui implémentent l'interface. Ces énumérations peuvent ensuite être utilisées partout où le type d'énumération de base peut être utilisé, en supposant que les API sont écrites en termes d'interface.
J'ai tendance à éviter les énumérations, car elles ne sont pas extensibles. Pour rester avec l'exemple de l'OP, si A est dans une bibliothèque et B dans votre propre code, vous ne pouvez pas étendre A s'il s'agit d'une énumération. Voici comment je remplace parfois les énumérations:
// access like enum: A.apublicclass A {publicstaticfinal A a =new A();publicstaticfinal A b =new A();publicstaticfinal A c =new A();/*
* In case you need to identify your constant
* in different JVMs, you need an id. This is the case if
* your object is transfered between
* different JVM instances (eg. save/load, or network).
* Also, switch statements don't work with
* Objects, but work with int.
*/publicstaticint maxId=0;publicint id = maxId++;publicint getId(){return id;}}publicclass B extends A {/*
* good: you can do like
* A x = getYourEnumFromSomeWhere();
* if(x instanceof B) ...;
* to identify which enum x
* is of.
*/publicstaticfinal A d =new A();}publicclass C extends A {/* Good: e.getId() != d.getId()
* Bad: in different JVMs, C and B
* might be initialized in different order,
* resulting in different IDs.
* Workaround: use a fixed int, or hash code.
*/publicstaticfinal A e =new A();publicint getId(){return-32489132;};}
Il y a quelques trous à éviter, voir les commentaires dans le code. Selon vos besoins, il s'agit d'une alternative solide et extensible aux énumérations.
cela peut être bien si vous avez juste besoin d'un ordinal pour les instances. Mais les énumérations ont également une propriété de nom qui est assez utile.
2018
6
C'est ainsi que j'améliore le modèle d'héritage enum avec la vérification d'exécution dans l'initialiseur statique. La BaseKind#checkEnumExtendervérification que l'énumération "extensible" déclare toutes les valeurs de l'énumération de base exactement de la même manière #name()et #ordinal()reste entièrement compatible.
Il y a toujours du copier-coller impliqué pour déclarer des valeurs mais le programme échoue rapidement si quelqu'un a ajouté ou modifié une valeur dans la classe de base sans mettre à jour les extensions.
Comportement courant pour différentes énumérations s'étendant les unes aux autres:
publicinterfaceKind{/**
* Let's say we want some additional member.
*/String description();/**
* Standard {@code Enum} method.
*/String name();/**
* Standard {@code Enum} method.
*/int ordinal();}
Enum de base, avec méthode de vérification:
publicenumBaseKindimplementsKind{
FIRST("First"),
SECOND("Second"),;privatefinalString description ;publicString description(){return description ;}privateBaseKind(finalString description ){this.description = description ;}publicstaticvoid checkEnumExtender(finalKind[] baseValues,finalKind[] extendingValues
){if( extendingValues.length < baseValues.length ){thrownewIncorrectExtensionError("Only "+ extendingValues.length +" values against "+ baseValues.length +" base values");}for(int i =0; i < baseValues.length ; i ++){finalKind baseValue = baseValues[ i ];finalKind extendingValue = extendingValues[ i ];if( baseValue.ordinal()!= extendingValue.ordinal()){thrownewIncorrectExtensionError("Base ordinal "+ baseValue.ordinal()+" doesn't match with "+ extendingValue.ordinal());}if(! baseValue.name().equals( extendingValue.name())){thrownewIncorrectExtensionError("Base name[ "+ i +"] "+ baseValue.name()+" doesn't match with "+ extendingValue.name());}if(! baseValue.description().equals( extendingValue.description())){thrownewIncorrectExtensionError("Description[ "+ i +"] "+ baseValue.description()+" doesn't match with "+ extendingValue.description());}}}publicstaticclassIncorrectExtensionErrorextendsError{publicIncorrectExtensionError(finalString s ){super( s );}}}
@AxelAdvento L'idée ici est que nous dépendons de l'interface Dayqui a valueOf()alors la méthode switch(Day.valueOf()), elle est implémentée par des WeekDay, WeekEndDayénumérations.
Khaled Lela
3
Je vous suggère d'adopter l'approche inverse.
Au lieu d'étendre l'énumération existante, créez-en une plus grande et créez-en un sous-ensemble. Par exemple, si vous aviez une énumération appelée PET et que vous vouliez l'étendre à ANIMAL, vous devriez le faire à la place:
publicenum ANIMAL {
WOLF,CAT, DOG
}EnumSet<ANIMAL> pets =EnumSet.of(ANIMAL.CAT, ANIMAL.DOG);
Attention, les animaux de compagnie ne sont pas des collections immuables, vous voudrez peut-être utiliser Guava ou Java9 pour plus de sécurité.
Ayant moi-même eu ce même problème, je voudrais publier mon point de vue. Je pense qu'il y a quelques facteurs de motivation pour faire quelque chose comme ça:
Vous voulez avoir des codes énumérés liés, mais dans différentes classes. Dans mon cas, j'avais une classe de base avec plusieurs codes définis dans une énumération associée. À une date ultérieure (aujourd'hui!), Je voulais fournir de nouvelles fonctionnalités à la classe de base, ce qui signifiait également de nouveaux codes pour l'énumération.
La classe dérivée prendrait en charge à la fois l'énumération des classes de base et la sienne. Pas de valeurs d'énumération en double! Donc: comment avoir une énumération pour la sous-classe qui inclut l'énumération de son parent avec ses nouvelles valeurs.
L'utilisation d'une interface ne le coupe pas vraiment: vous pouvez accidentellement obtenir des valeurs d'énumération en double. Pas souhaitable.
J'ai fini par combiner les énumérations: cela garantit qu'il ne peut y avoir de valeurs en double, au détriment d'être moins étroitement lié à sa classe associée. Mais, je pensais que le problème des doublons était ma principale préoccupation ...
Pour comprendre pourquoi l'extension d'une énumération n'est pas raisonnable au niveau de l'implémentation du langage, il faut considérer ce qui se passerait si vous passiez une instance de l'énumération étendue à une routine qui ne comprend que l'énumération de base. Un commutateur promis par le compilateur dans tous les cas ne couvrirait en fait pas ces valeurs Enum étendues.
Cela souligne en outre que les valeurs Java Enum ne sont pas des entiers tels que les C le sont, par exemple: pour utiliser un Java Enum comme index de tableau, vous devez explicitement demander son membre ordinal (), pour donner à java Enum une valeur entière arbitraire que vous devez ajouter un champ explicite pour cela et référence ce membre nommé.
Ce n'est pas un commentaire sur le désir du PO, juste sur la raison pour laquelle Java ne le fera jamais.
Dans l'espoir que cette élégante solution d'un de mes collègues soit même vue dans ce long post, je voudrais partager cette approche pour le sous-classement qui suit l'approche d'interface et au-delà.
Veuillez noter que nous utilisons des exceptions personnalisées ici et que ce code ne sera pas compilé à moins que vous ne le remplaciez par vos exceptions.
La documentation est vaste et j'espère qu'elle est compréhensible pour la plupart d'entre vous.
L'interface que chaque énumération sous-classée doit implémenter.
publicinterfaceParameter{/**
* Retrieve the parameters name.
*
* @return the name of the parameter
*/String getName();/**
* Retrieve the parameters type.
*
* @return the {@link Class} according to the type of the parameter
*/Class<?> getType();/**
* Matches the given string with this parameters value pattern (if applicable). This helps to find
* out if the given string is a syntactically valid candidate for this parameters value.
*
* @param valueStr <i>optional</i> - the string to check for
* @return <code>true</code> in case this parameter has no pattern defined or the given string
* matches the defined one, <code>false</code> in case <code>valueStr</code> is
* <code>null</code> or an existing pattern is not matched
*/boolean match(finalString valueStr);/**
* This method works as {@link #match(String)} but throws an exception if not matched.
*
* @param valueStr <i>optional</i> - the string to check for
* @throws ArgumentException with code
* <dl>
* <dt>PARAM_MISSED</dt>
* <dd>if <code>valueStr</code> is <code>null</code></dd>
* <dt>PARAM_BAD</dt>
* <dd>if pattern is not matched</dd>
* </dl>
*/void matchEx(finalString valueStr)throwsArgumentException;/**
* Parses a value for this parameter from the given string. This method honors the parameters data
* type and potentially other criteria defining a valid value (e.g. a pattern).
*
* @param valueStr <i>optional</i> - the string to parse the parameter value from
* @return the parameter value according to the parameters type (see {@link #getType()}) or
* <code>null</code> in case <code>valueStr</code> was <code>null</code>.
* @throws ArgumentException in case <code>valueStr</code> is not parsable as a value for this
* parameter.
*/Object parse(finalString valueStr)throwsArgumentException;/**
* Converts the given value to its external form as it is accepted by {@link #parse(String)}. For
* most (ordinary) parameters this is simply a call to {@link String#valueOf(Object)}. In case the
* parameter types {@link Object#toString()} method does not return the external form (e.g. for
* enumerations), this method has to be implemented accordingly.
*
* @param value <i>mandatory</i> - the parameters value
* @return the external form of the parameters value, never <code>null</code>
* @throws InternalServiceException in case the given <code>value</code> does not match
* {@link #getType()}
*/String toString(finalObject value)throwsInternalServiceException;}
La classe de base ENUM implémentée.
publicenumParametersimplementsParameter{/**
* ANY ENUM VALUE
*/
VALUE(newParameterImpl<String>("VALUE",String.class,"[A-Za-z]{3,10}"));/**
* The parameter wrapped by this enum constant.
*/privateParameter param;/**
* Constructor.
*
* @param param <i>mandatory</i> - the value for {@link #param}
*/privateParameters(finalParameter param){this.param = param;}/**
* {@inheritDoc}
*/@OverridepublicString getName(){returnthis.param.getName();}/**
* {@inheritDoc}
*/@OverridepublicClass<?> getType(){returnthis.param.getType();}/**
* {@inheritDoc}
*/@Overridepublicboolean match(finalString valueStr){returnthis.param.match(valueStr);}/**
* {@inheritDoc}
*/@Overridepublicvoid matchEx(finalString valueStr){this.param.matchEx(valueStr);}/**
* {@inheritDoc}
*/@OverridepublicObject parse(finalString valueStr)throwsArgumentException{returnthis.param.parse(valueStr);}/**
* {@inheritDoc}
*/@OverridepublicString toString(finalObject value)throwsInternalServiceException{returnthis.param.toString(value);}}
ENUM sous-classé qui "hérite" de la classe de base.
publicenumExtendedParametersimplementsParameter{/**
* ANY ENUM VALUE
*/
VALUE(my.package.name.VALUE);/**
* EXTENDED ENUM VALUE
*/
EXTENDED_VALUE(newParameterImpl<String>("EXTENDED_VALUE",String.class,"[0-9A-Za-z_.-]{1,20}"));/**
* The parameter wrapped by this enum constant.
*/privateParameter param;/**
* Constructor.
*
* @param param <i>mandatory</i> - the value for {@link #param}
*/privateParameters(finalParameter param){this.param = param;}/**
* {@inheritDoc}
*/@OverridepublicString getName(){returnthis.param.getName();}/**
* {@inheritDoc}
*/@OverridepublicClass<?> getType(){returnthis.param.getType();}/**
* {@inheritDoc}
*/@Overridepublicboolean match(finalString valueStr){returnthis.param.match(valueStr);}/**
* {@inheritDoc}
*/@Overridepublicvoid matchEx(finalString valueStr){this.param.matchEx(valueStr);}/**
* {@inheritDoc}
*/@OverridepublicObject parse(finalString valueStr)throwsArgumentException{returnthis.param.parse(valueStr);}/**
* {@inheritDoc}
*/@OverridepublicString toString(finalObject value)throwsInternalServiceException{returnthis.param.toString(value);}}
Enfin le ParameterImpl générique pour ajouter quelques utilitaires.
publicclassParameterImpl<T>implementsParameter{/**
* The default pattern for numeric (integer, long) parameters.
*/privatestaticfinalPattern NUMBER_PATTERN =Pattern.compile("[0-9]+");/**
* The default pattern for parameters of type boolean.
*/privatestaticfinalPattern BOOLEAN_PATTERN =Pattern.compile("0|1|true|false");/**
* The name of the parameter, never <code>null</code>.
*/privatefinalString name;/**
* The data type of the parameter.
*/privatefinalClass<T> type;/**
* The validation pattern for the parameters values. This may be <code>null</code>.
*/privatefinalPattern validator;/**
* Shortcut constructor without <code>validatorPattern</code>.
*
* @param name <i>mandatory</i> - the value for {@link #name}
* @param type <i>mandatory</i> - the value for {@link #type}
*/publicParameterImpl(finalString name,finalClass<T> type){this(name, type,null);}/**
* Constructor.
*
* @param name <i>mandatory</i> - the value for {@link #name}
* @param type <i>mandatory</i> - the value for {@link #type}
* @param validatorPattern - <i>optional</i> - the pattern for {@link #validator}
* <dl>
* <dt style="margin-top:0.25cm;"><i>Note:</i>
* <dd>The default validation patterns {@link #NUMBER_PATTERN} or
* {@link #BOOLEAN_PATTERN} are applied accordingly.
* </dl>
*/publicParameterImpl(finalString name,finalClass<T> type,finalString validatorPattern){this.name = name;this.type = type;if(null!= validatorPattern){this.validator =Pattern.compile(validatorPattern);}elseif(Integer.class==this.type ||Long.class==this.type){this.validator = NUMBER_PATTERN;}elseif(Boolean.class==this.type){this.validator = BOOLEAN_PATTERN;}else{this.validator =null;}}/**
* {@inheritDoc}
*/@Overridepublicboolean match(finalString valueStr){if(null== valueStr){returnfalse;}if(null!=this.validator){finalMatcher matcher =this.validator.matcher(valueStr);return matcher.matches();}returntrue;}/**
* {@inheritDoc}
*/@Overridepublicvoid matchEx(finalString valueStr)throwsArgumentException{if(false==this.match(valueStr)){if(null== valueStr){throwArgumentException.createEx(ErrorCode.PARAM_MISSED,"The value must not be null",this.name);}throwArgumentException.createEx(ErrorCode.PARAM_BAD,"The value must match the pattern: "+this.validator.pattern(),this.name);}}/**
* Parse the parameters value from the given string value according to {@link #type}. Additional
* the value is checked by {@link #matchEx(String)}.
*
* @param valueStr <i>optional</i> - the string value to parse the value from
* @return the parsed value, may be <code>null</code>
* @throws ArgumentException in case the parameter:
* <ul>
* <li>does not {@link #matchEx(String)} the {@link #validator}</li>
* <li>cannot be parsed according to {@link #type}</li>
* </ul>
* @throws InternalServiceException in case the type {@link #type} cannot be handled. This is a
* programming error.
*/@Overridepublic T parse(finalString valueStr)throwsArgumentException,InternalServiceException{if(null== valueStr){returnnull;}this.matchEx(valueStr);if(String.class==this.type){returnthis.type.cast(valueStr);}if(Boolean.class==this.type){returnthis.type.cast(Boolean.valueOf(("1".equals(valueStr))||Boolean.valueOf(valueStr)));}try{if(Integer.class==this.type){returnthis.type.cast(Integer.valueOf(valueStr));}if(Long.class==this.type){returnthis.type.cast(Long.valueOf(valueStr));}}catch(finalNumberFormatException e){throwArgumentException.createEx(ErrorCode.PARAM_BAD,"The value cannot be parsed as "+this.type.getSimpleName().toLowerCase()+".",this.name);}returnthis.parseOther(valueStr);}/**
* Field access for {@link #name}.
*
* @return the value of {@link #name}.
*/@OverridepublicString getName(){returnthis.name;}/**
* Field access for {@link #type}.
*
* @return the value of {@link #type}.
*/@OverridepublicClass<T> getType(){returnthis.type;}/**
* {@inheritDoc}
*/@OverridepublicfinalString toString(finalObject value)throwsInternalServiceException{if(false==this.type.isAssignableFrom(value.getClass())){thrownewInternalServiceException(ErrorCode.PANIC,"Parameter.toString(): Bad type of value. Expected {0} but is {1}.",this.type.getName(),
value.getClass().getName());}if(String.class==this.type ||Integer.class==this.type ||Long.class==this.type){returnString.valueOf(value);}if(Boolean.class==this.type){returnBoolean.TRUE.equals(value)?"1":"0";}returnthis.toStringOther(value);}/**
* Parse parameter values of other (non standard types). This method is called by
* {@link #parse(String)} in case {@link #type} is none of the supported standard types (currently
* String, Boolean, Integer and Long). It is intended for extensions.
* <dl>
* <dt style="margin-top:0.25cm;"><i>Note:</i>
* <dd>This default implementation always throws an InternalServiceException.
* </dl>
*
* @param valueStr <i>mandatory</i> - the string value to parse the value from
* @return the parsed value, may be <code>null</code>
* @throws ArgumentException in case the parameter cannot be parsed according to {@link #type}
* @throws InternalServiceException in case the type {@link #type} cannot be handled. This is a
* programming error.
*/protected T parseOther(finalString valueStr)throwsArgumentException,InternalServiceException{thrownewInternalServiceException(ErrorCode.PANIC,"ParameterImpl.parseOther(): Unsupported parameter type: "+this.type.getName());}/**
* Convert the values of other (non standard types) to their external form. This method is called
* by {@link #toString(Object)} in case {@link #type} is none of the supported standard types
* (currently String, Boolean, Integer and Long). It is intended for extensions.
* <dl>
* <dt style="margin-top:0.25cm;"><i>Note:</i>
* <dd>This default implementation always throws an InternalServiceException.
* </dl>
*
* @param value <i>mandatory</i> - the parameters value
* @return the external form of the parameters value, never <code>null</code>
* @throws InternalServiceException in case the given <code>value</code> does not match
* {@link #getClass()}
*/protectedString toStringOther(finalObject value)throwsInternalServiceException{thrownewInternalServiceException(ErrorCode.PANIC,"ParameterImpl.toStringOther(): Unsupported parameter type: "+this.type.getName());}}
// enum A { a, b, c }staticfinalSet<Short> enumA =newLinkedHashSet<>(Arrays.asList(newShort[]{'a','b','c'}));// enum B extends A { d }staticfinalSet<Short> enumB =newLinkedHashSet<>(enumA);static{
enumB.add((short)'d');// If you have to add more elements:// enumB.addAll(Arrays.asList(new Short[]{ 'e', 'f', 'g', '♯', '♭' }));}
LinkedHashSetprévoit à la fois que chaque entrée n'existe qu'une seule fois et que leur ordre est conservé. Si l'ordre n'a pas d'importance, vous pouvez utiliser à la HashSetplace. Le code suivant n'est pas possible en Java:
for(A a : B.values()){// enum B extends A { d }switch(a){case a:case b:case c:System.out.println("Value is: "+ a.toString());break;default:thrownewIllegalStateException("This should never happen.");}}
Le code peut être écrit comme suit:
for(Short a : enumB){switch(a){case'a':case'b':case'c':System.out.println("Value is: "+newString(Character.toChars(a)));break;default:thrownewIllegalStateException("This should never happen.");}}
À partir de Java 7, vous pouvez même faire de même avec String:
// enum A { BACKWARDS, FOREWARDS, STANDING }staticfinalSet<String> enumA =newLinkedHashSet<>(Arrays.asList(newString[]{"BACKWARDS","FOREWARDS","STANDING"}));// enum B extends A { JUMP }staticfinalSet<String> enumB =newLinkedHashSet<>(enumA);static{
enumB.add("JUMP");}
Utilisation du remplacement d'énumération:
for(String a : enumB){switch(a){case"BACKWARDS":case"FOREWARDS":case"STANDING":System.out.println("Value is: "+ a);break;default:thrownewIllegalStateException("This should never happen.");}}
PrimaryColours
; il est raisonnable de vouloir super -class cela EnumPrimaryAndPastelColours
en ajoutant de nouveaux noms de couleurs. Liskov est toujours l'éléphant dans la pièce. Alors pourquoi ne pas commencer par une énumération de base de:AllMyColours
- Et puis on pourrait sous- classer toutes les couleurs à:PrimaryAndPastelColours
et ensuite sous- classer ceci à:PrimaryColours
(en gardant la hiérarchie à l'esprit). Java ne le permettra pas non plus.Réponses:
Non, vous ne pouvez pas faire cela en Java. En dehors de toute autre chose,
d
serait alors vraisemblablement une instance deA
(étant donné l'idée normale de "s'étend"), mais les utilisateurs qui ne connaissaient queA
ne le sauraient pas - ce qui va à l'encontre de l'énumération étant un ensemble bien connu de valeurs.Si vous pouviez nous en dire plus sur la façon dont vous souhaitez l' utiliser , nous pourrions potentiellement suggérer des solutions alternatives.
la source
Object->A->B
au lieu deObject->A->B extends Object
Les énumérations représentent une énumération complète des valeurs possibles. La réponse (inutile) est donc non.
Comme exemple de problème réel, prenez les jours de la semaine, les jours de week-end et, le syndicat, les jours de semaine. Nous pourrions définir tous les jours dans les jours de la semaine, mais nous ne serions pas en mesure de représenter des propriétés spéciales pour les jours de la semaine et les week-ends.
Ce que nous pourrions faire, c'est avoir trois types d'énumération avec une correspondance entre les jours de la semaine / week-end et les jours de la semaine.
Alternativement, nous pourrions avoir une interface ouverte pour le jour de la semaine:
Ou nous pourrions combiner les deux approches:
la source
La solution recommandée est le modèle d'énumération extensible .
Cela implique la création d'une interface et son utilisation là où vous utilisez actuellement l'énumération. Ensuite, faites enum implémenter l'interface. Vous pouvez ajouter plus de constantes en faisant en sorte que cette nouvelle énumération étende également l'interface.
la source
Sous les couvertures, votre ENUM n'est qu'une classe régulière générée par le compilateur. Cette classe générée s'étend
java.lang.Enum
. La raison technique pour laquelle vous ne pouvez pas étendre la classe générée est que la classe générée l'estfinal
. Les raisons conceptuelles pour lesquelles il est définitif sont discutées dans cette rubrique. Mais je vais ajouter la mécanique à la discussion.Voici une énumération de tests:
Le code résultant de javap:
En théorie, vous pouvez taper cette classe par vous-même et supprimer la "finale". Mais le compilateur vous empêche d'étendre directement "java.lang.Enum". Vous pourriez décider de NE PAS étendre java.lang.Enum, mais votre classe et ses classes dérivées ne seraient pas une instance de java.lang.Enum ... ce qui pourrait ne pas vraiment vous intéresser!
la source
peut s'écrire:
Comment cela peut être utile: Disons que nous voulons quelque chose comme: Nous avons des événements et nous utilisons des énumérations. Ces énumérations peuvent être regroupées par un traitement similaire. Si nous avons une opération avec de nombreux éléments, certains événements démarrent l'opération, certains ne sont qu'une étape et d'autres mettent fin à l'opération. Pour rassembler une telle opération et éviter un long commutateur, nous pouvons les regrouper comme dans l'exemple et utiliser:
Exemple:
Ajoutez un peu plus avancé:
Au-dessus si nous avons un échec (myEvent.is (State_StatusGroup.FAIL)) puis en itérant par les événements précédents, nous pouvons facilement vérifier si nous devons annuler le transfert d'argent en:
Il peut être utile pour:
la source
Voici comment j'ai trouvé comment étendre un enum à un autre enum, c'est une approche très directe:
Supposons que vous ayez une énumération avec des constantes communes:
alors vous pouvez essayer de faire un manuel s’étend de cette façon:
bien sûr, chaque fois que vous devez étendre une constante, vous devez modifier vos fichiers SubEnum.
la source
Au cas où vous l'auriez manqué, il y a un chapitre dans l'excellent livre de Joshua Bloch " Java Effective, 2nd edition ".
Extrayez ici .
Juste la conclusion:
la source
J'ai tendance à éviter les énumérations, car elles ne sont pas extensibles. Pour rester avec l'exemple de l'OP, si A est dans une bibliothèque et B dans votre propre code, vous ne pouvez pas étendre A s'il s'agit d'une énumération. Voici comment je remplace parfois les énumérations:
Il y a quelques trous à éviter, voir les commentaires dans le code. Selon vos besoins, il s'agit d'une alternative solide et extensible aux énumérations.
la source
C'est ainsi que j'améliore le modèle d'héritage enum avec la vérification d'exécution dans l'initialiseur statique. La
BaseKind#checkEnumExtender
vérification que l'énumération "extensible" déclare toutes les valeurs de l'énumération de base exactement de la même manière#name()
et#ordinal()
reste entièrement compatible.Il y a toujours du copier-coller impliqué pour déclarer des valeurs mais le programme échoue rapidement si quelqu'un a ajouté ou modifié une valeur dans la classe de base sans mettre à jour les extensions.
Comportement courant pour différentes énumérations s'étendant les unes aux autres:
Enum de base, avec méthode de vérification:
Exemple d'extension:
la source
Basé sur @Tom Hawtin - réponse sur la ligne de touche, nous ajoutons la prise en charge du commutateur
la source
valueOf()
méthode?Day
qui avalueOf()
alors la méthodeswitch(Day.valueOf())
, elle est implémentée par desWeekDay, WeekEndDay
énumérations.Je vous suggère d'adopter l'approche inverse.
Au lieu d'étendre l'énumération existante, créez-en une plus grande et créez-en un sous-ensemble. Par exemple, si vous aviez une énumération appelée PET et que vous vouliez l'étendre à ANIMAL, vous devriez le faire à la place:
Attention, les animaux de compagnie ne sont pas des collections immuables, vous voudrez peut-être utiliser Guava ou Java9 pour plus de sécurité.
la source
Ayant moi-même eu ce même problème, je voudrais publier mon point de vue. Je pense qu'il y a quelques facteurs de motivation pour faire quelque chose comme ça:
L'utilisation d'une interface ne le coupe pas vraiment: vous pouvez accidentellement obtenir des valeurs d'énumération en double. Pas souhaitable.
J'ai fini par combiner les énumérations: cela garantit qu'il ne peut y avoir de valeurs en double, au détriment d'être moins étroitement lié à sa classe associée. Mais, je pensais que le problème des doublons était ma principale préoccupation ...
la source
Pour comprendre pourquoi l'extension d'une énumération n'est pas raisonnable au niveau de l'implémentation du langage, il faut considérer ce qui se passerait si vous passiez une instance de l'énumération étendue à une routine qui ne comprend que l'énumération de base. Un commutateur promis par le compilateur dans tous les cas ne couvrirait en fait pas ces valeurs Enum étendues.
Cela souligne en outre que les valeurs Java Enum ne sont pas des entiers tels que les C le sont, par exemple: pour utiliser un Java Enum comme index de tableau, vous devez explicitement demander son membre ordinal (), pour donner à java Enum une valeur entière arbitraire que vous devez ajouter un champ explicite pour cela et référence ce membre nommé.
Ce n'est pas un commentaire sur le désir du PO, juste sur la raison pour laquelle Java ne le fera jamais.
la source
Dans l'espoir que cette élégante solution d'un de mes collègues soit même vue dans ce long post, je voudrais partager cette approche pour le sous-classement qui suit l'approche d'interface et au-delà.
Veuillez noter que nous utilisons des exceptions personnalisées ici et que ce code ne sera pas compilé à moins que vous ne le remplaciez par vos exceptions.
La documentation est vaste et j'espère qu'elle est compréhensible pour la plupart d'entre vous.
L'interface que chaque énumération sous-classée doit implémenter.
La classe de base ENUM implémentée.
ENUM sous-classé qui "hérite" de la classe de base.
Enfin le ParameterImpl générique pour ajouter quelques utilitaires.
la source
Ma façon de coder ce serait la suivante:
LinkedHashSet
prévoit à la fois que chaque entrée n'existe qu'une seule fois et que leur ordre est conservé. Si l'ordre n'a pas d'importance, vous pouvez utiliser à laHashSet
place. Le code suivant n'est pas possible en Java:Le code peut être écrit comme suit:
À partir de Java 7, vous pouvez même faire de même avec
String
:Utilisation du remplacement d'énumération:
la source