Una razón para hacerlo es probar la situación en la que hay un valor de enumeración no válido sin introducir un valor de enumeración no válido en el origen del núcleo.
Arquímedes Trajano
Sí, un ejemplo de pureza "lingüística". Creo que lo que se desea es la idea de ahorro de trabajo "bookeeping" de un conjunto de enteros de incremento automático como uno tiene en C ++ para que pueda comenzar un nuevo conjunto como una extensión del conjunto anterior que comienza en el 1+ el último valor del conjunto anterior, y si las entradas se nombran heredan los nombres del "subconjunto común". Aunque la enumeración de Java tiene algunas cosas buenas al respecto, carece de la ayuda simple y automática de incremento automático que declara la ayuda que proporciona la enumeración de C ++.
Peter
44
En realidad, cuando amplías tu enumeración con nuevos valores, no estás creando una subclase, sino una superclase. Puede usar valores de enumeración base en todas partes en lugar de enumeración "extendida", pero no al revés, por lo que de acuerdo con el Principio de sustitución de Liskov, la enumeración extendida es la superclase de enumeración base.
Ilya
@Ilya ... sí, eso es cierto. Señalo que la pregunta tiene casos de uso definidos en el mundo real. Por motivo de la discusión, considere una base de Enumeración de: PrimaryColours; es razonable querer súper -class esto a Enum PrimaryAndPastelColoursmediante la adición de nuevos nombres de colores. Liskov sigue siendo el elefante en la habitación. ¿Por qué no comenzar con una base de enumeración de: AllMyColours- Y entonces un poder sub -class todos los colores a: PrimaryAndPastelColoursy, posteriormente, sub -class esto: PrimaryColours(manteniendo la jerarquía en cuenta). Sin embargo, Java tampoco lo permitirá.
será
Respuestas:
451
No, no puedes hacer esto en Java. Aparte de cualquier otra cosa, dpresumiblemente sería una instancia de A(dada la idea normal de "extensiones"), pero los usuarios que solo sabían sobre Aesto no lo sabrían, lo que frustra el punto de que un enum sea un conjunto bien conocido de valores.
Si pudiera contarnos más acerca de cómo desea usar esto, podríamos sugerir soluciones alternativas.
Todas las enumeraciones extienden implícitamente java.lang.Enum. Como Java no admite la herencia múltiple, una enumeración no puede extender nada más.
givanse
99
La razón por la que quiero extender es porque me gustaría tener una clase base llamada, por ejemplo, IntEnum, que se ve así: stackoverflow.com/questions/1681976/enum-with-int-value-in-java/… . Entonces todas mis enumeraciones podrían extenderlo ... en este caso, solo me beneficiaría de la herencia y, por lo tanto, no tendría que duplicar este código de "enumeración basada en int" con frecuencia. Soy nuevo en Java y vengo de C #, y espero perder algo. Mi opinión actual es que las enumeraciones de Java son un dolor en comparación con C #.
Tyler Collier el
30
@ Tyler: las enumeraciones de C # son solo nombres asociados con números, sin validación automática ni nada . Las enumeraciones IMO son el único bit de Java que en realidad es mejor que C #.
Jon Skeet
21
No estoy de acuerdo con @JonSkeet aquí. En mi caso de uso, me gustaría separar toda la lógica desagradable en mi gran enumeración, y tener la lógica oculta, y definir una enumeración limpia que extienda la otra que está oculta. Las enumeraciones con mucha lógica superan la idea de tener variables limpias declaradas para que no tenga que declarar cientos de variables de cadenas estáticas para que una clase con 5 enumeraciones no se vuelva ilegible y demasiado grande en las líneas. No quiero que los otros desarrolladores se preocupen por copiar y pegar esa paz de código para el próximo proyecto tampoco y, en cambio, extiendan el base_enum ... tiene sentido para mí ...
mmm
43
@givanse ... no estoy de acuerdo contigo en el punto de la extensión implícita de java.lang.Enum es la causa de la no herencia ya que todas las clases en Java también heredan implícitamente la clase Object, pero pueden heredar alguna otra clase, ya que luego vendría en la jerarquía como en Object->A->Blugar deObject->A->B extends Object
mickeymoon
317
Las enumeraciones representan una enumeración completa de los posibles valores. Entonces la respuesta (inútil) es no.
Como ejemplo de un problema real, tome los días de semana, los días de fin de semana y, la unión, los días de la semana. Podríamos definir todos los días dentro de los días de la semana, pero no podríamos representar propiedades especiales para los días de la semana y los días de fin de semana.
Lo que podríamos hacer es tener tres tipos de enumeración con un mapeo entre días de la semana / días de fin de semana y días de la semana.
¿No hay un problema con esto? Una instrucción switch no funcionará en una interfaz, pero funciona en una enumeración normal. No funciona con el tipo de interruptor mata una de las mejores cosas de las enumeraciones.
Manius
99
Estoy pensando que podría haber otro problema con esto. No hay igualdad entre Weekday.MON y DayOfWeek.MON. ¿No es ese el otro gran beneficio de las enumeraciones? No tengo una solución mejor, solo me doy cuenta de esto mientras trato de encontrar la mejor respuesta. La falta de poder usar == fuerza un poco la mano.
Snekse
2
@Crusader sí, esa es precisamente la compensación. Si desea algo expandible, no puede tener sentencias de cambio fijas, si desea un conjunto de valores conocidos fijos, tautológicamente no puede tener algo expandible.
djechlin
3
Al pasar de enum a interface, también pierde la llamada estática a los valores (). Esto dificulta la refactorización, especialmente si decide extender su enumeración y agregar la interfaz como una barrera de abstracción a una enumeración establecida.
Joshua Goldberg
44
Este enfoque de derivar una enumeración de una interfaz es utilizado por la API de Java 1.7, por ejemplo, java.nio.file.Files.write () toma una matriz de OpenOption como último argumento. OpenOption es una interfaz, pero cuando llamamos a esta función, generalmente pasamos una constante de enumeración StandardOpenOption, que se deriva de OpenOption. Esto tiene la ventaja de ser extensible, pero también tiene inconvenientes. La implementación adolece del hecho de que OpenOption es una interfaz. Crea un HashSet <OpenOption> a partir de la matriz pasada, cuando podría haber creado un EnumSet más eficiente en espacio y tiempo. Y no puede usar el interruptor.
Esto implica crear una interfaz y usarla donde actualmente usa la enumeración. Luego haga que la enumeración implemente la interfaz. Puede agregar más constantes haciendo que esa nueva enumeración también extienda la interfaz.
Vale la pena mencionar su uso de un método de fábrica en la interfaz. Gran manera de compartir funcionalidades comunes entre Enums relacionados dado que extender no es una solución viable.
Tim Clemons
8
¿Puede proporcionar más detalles (código :)) sobre este patrón?
Dherik
3
Ese patrón no permite extender los valores de una enumeración. Cuál es el punto en la pregunta formulada.
Eria
55
Debajo de las cubiertas, su ENUM es solo una clase regular generada por el compilador. Esa clase generada se extiende java.lang.Enum. La razón técnica por la que no puede extender la clase generada es que la clase generada es final. Las razones conceptuales para que sea final se discuten en este tema. Pero agregaré la mecánica a la discusión.
Aquí hay una enumeración de prueba:
publicenum TEST {
ONE, TWO, THREE;}
El código resultante 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);}
Posiblemente podría escribir esta clase por su cuenta y soltar el "final". Pero el compilador le impide extender "java.lang.Enum" directamente. Podrías decidir NO extender java.lang.Enum, pero entonces tu clase y sus clases derivadas no serían una instancia de java.lang.Enum ... ¡lo cual realmente no te importaría de ninguna manera!
¿Qué está haciendo el bloque estático vacío? 'static {};'
hollín
1
No tiene código en él. El programa "javap" muestra el bloque vacío.
ChrisCantrell
Es extraño tenerlo allí si no está haciendo nada, ¿no?
hollín
44
¡Tienes razón! Mi error. NO es un bloque de código vacío. Si ejecuta "javap -c" verá el código real dentro del bloque estático. El bloque estático crea todas las instancias ENUM (UNA, DOS y TRES aquí). Lo siento por eso.
ChrisCantrell
1
Gracias por afirmar el hecho: porque java.lang.Enum se declara final.
Benjamin
26
enum A {a,b,c}enum B extends A {d}/*B is {a,b,c,d}*/
Se puede escribir como:
publicenumAll{
a (ClassGroup.A,ClassGroup.B),
b (ClassGroup.A,ClassGroup.B),
c (ClassGroup.A,ClassGroup.B),
d (ClassGroup.B)...
ClassGroup.B.getMembers () contiene {a, b, c, d}
Cómo puede ser útil: Digamos que queremos algo como: Tenemos eventos y estamos usando enumeraciones. Esas enumeraciones se pueden agrupar por procesamiento similar. Si tenemos operación con muchos elementos, algunos eventos comienzan a funcionar, algunos son solo pasos y otros finalizan la operación. Para recopilar dicha operación y evitar un caso de cambio largo, podemos agruparlos como en el ejemplo y usar:
En el caso anterior, si tenemos algún error (myEvent.is (State_StatusGroup.FAIL)), iterando por eventos anteriores podemos verificar fácilmente si debemos revertir la transferencia de dinero:
interesante, podríamos usar también la enumeración toString (), y al final comparar cadenas; y para usar el interruptor, solo tendríamos que lanzar el objeto a una enumeración conocida; el único problema sería que 2 desarrolladores extendieran y crearan una identificación de enumeración idéntica, y luego trataran de fusionar ambos códigos :), ahora creo que entiendo por qué la enumeración no debe ser extensible.
Acuario Power
11
En caso de que te lo hayas perdido, hay un capítulo en el excelente libro de Joshua Bloch " Java Effective, 2nd edition ".
Capítulo 6 - Enums y anotaciones
Artículo 34: emule enumeraciones extensibles con interfaces
Una desventaja menor del uso de interfaces para emular enumeraciones extensibles es que las implementaciones no pueden heredarse de un tipo de enumeración a otro. En el caso de nuestro ejemplo de Operación, la lógica para almacenar y recuperar el símbolo asociado con una operación se duplica en BasicOperation y ExtendedOperation. En este caso no importa porque se duplica muy poco código. Si hubiera una mayor cantidad de funcionalidad compartida, podría encapsularla en una clase auxiliar o un método auxiliar estático para eliminar la duplicación de código.
En resumen, aunque no puede escribir un tipo de enumeración extensible, puede emularlo escribiendo una interfaz que vaya con un tipo de enumeración básico que implemente la interfaz. Esto permite a los clientes escribir sus propias enumeraciones que implementan la interfaz. Estas enumeraciones se pueden usar siempre que se pueda usar el tipo de enumeración básico, suponiendo que las API se escriban en términos de la interfaz.
Tiendo a evitar las enumeraciones, porque no son extensibles. Para seguir con el ejemplo del OP, si A está en una biblioteca y B en su propio código, no puede extender A si es una enumeración. Así es como a veces reemplazo las enumeraciones:
// 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;};}
Hay algunos hoyos para evitar, vea los comentarios en el código. Dependiendo de sus necesidades, esta es una alternativa sólida y extensible a las enumeraciones.
puede estar bien si solo necesita algún ordinal para las instancias. Pero las enumeraciones también tienen una propiedad de nombre que es bastante útil.
desde el
6
Así es como mejoro el patrón de herencia enum con la verificación de tiempo de ejecución en el inicializador estático. Las BaseKind#checkEnumExtendercomprobaciones de que la "extensión" enum declara todos los valores de la base enum exactamente de la misma manera #name()y #ordinal()siguen siendo totalmente compatibles.
Todavía hay que copiar y pegar para declarar valores, pero el programa falla rápidamente si alguien agrega o modifica un valor en la clase base sin actualizar los que se extienden.
Comportamiento común para diferentes enumeraciones que se extienden entre sí:
publicinterfaceKind{/**
* Let's say we want some additional member.
*/String description();/**
* Standard {@code Enum} method.
*/String name();/**
* Standard {@code Enum} method.
*/int ordinal();}
Base enum, con método de verificación:
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 La idea aquí es que dependemos de la interfaz Dayque tiene el método valueOf()a continuación switch(Day.valueOf()), se implementa mediante WeekDay, WeekEndDayenumeraciones.
Khaled Lela
3
Le sugiero que tome el enfoque inverso.
En lugar de extender la enumeración existente, cree una más grande y cree un subconjunto de la misma. Por ejemplo, si tenía una enumeración llamada PET y desea extenderla a ANIMAL, debe hacer esto en su lugar:
publicenum ANIMAL {
WOLF,CAT, DOG
}EnumSet<ANIMAL> pets =EnumSet.of(ANIMAL.CAT, ANIMAL.DOG);
Tenga cuidado, las mascotas no son colecciones inmutables, es posible que desee utilizar Guava o Java9 para mayor seguridad.
Habiendo tenido este mismo problema, me gustaría publicar mi perspectiva. Creo que hay un par de factores motivadores para hacer algo como esto:
Desea tener algunos códigos de enumeración relacionados, pero en diferentes clases. En mi caso, tenía una clase base con varios códigos definidos en una enumeración asociada. En una fecha posterior (¡hoy!), Quería proporcionar una nueva funcionalidad a la clase base, lo que también significaba nuevos códigos para la enumeración.
La clase derivada admitiría tanto la enumeración de las clases base como la suya. ¡No hay valores de enumeración duplicados! Entonces: cómo tener una enumeración para la subclase que incluye las enumeraciones de su padre junto con sus nuevos valores.
El uso de una interfaz realmente no es suficiente: accidentalmente puede obtener valores de enumeración duplicados. No deseable.
Terminé simplemente combinando las enumeraciones: esto garantiza que no pueda haber valores duplicados, a expensas de estar menos estrechamente vinculado a su clase asociada. Pero, pensé que el problema duplicado era mi principal preocupación ...
Como ayuda para comprender por qué extender una Enum no es razonable en el nivel de implementación del lenguaje para considerar lo que sucedería si pasara una instancia de la Enum extendida a una rutina que solo comprende la Enum base. Un cambio que el compilador prometió tenía todos los casos cubiertos, de hecho, no cubriría esos valores extendidos de Enum.
Esto enfatiza aún más que los valores de Java Enum no son enteros como los de C, por ejemplo: para usar un Java Enum como un índice de matriz, debe solicitar explícitamente su miembro ordinal (), para darle a un Java Enum un valor entero arbitrario que debe agregar un campo explícito para eso y referencia a ese miembro nombrado.
Este no es un comentario sobre el deseo del OP, solo sobre por qué Java nunca lo va a hacer.
Con la esperanza de que esta elegante solución de un colega mío se vea incluso en esta larga publicación, me gustaría compartir este enfoque para la subclasificación que sigue el enfoque de la interfaz y más allá.
Tenga en cuenta que aquí utilizamos excepciones personalizadas y este código no se compilará a menos que lo reemplace con sus excepciones.
La documentación es extensa y espero que sea comprensible para la mayoría de ustedes.
La interfaz que cada enumeración subclase necesita implementar.
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 implementación de la clase base ENUM.
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);}}
El ENUM subclasificado que "hereda" de la clase 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);}}
Finalmente el genérico ParameterImpl para agregar algunas utilidades.
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', '♯', '♭' }));}
LinkedHashSetproporciona tanto que cada entrada solo existe una vez, y que su orden se conserva. Si el orden no importa, puedes usarlo HashSeten su lugar. El siguiente código no es posible 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.");}}
El código se puede escribir de la siguiente manera:
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.");}}
Desde Java 7 en adelante, incluso puede hacer lo mismo con 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");}
Usando el reemplazo de enumeración:
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
; es razonable querer súper -class esto a EnumPrimaryAndPastelColours
mediante la adición de nuevos nombres de colores. Liskov sigue siendo el elefante en la habitación. ¿Por qué no comenzar con una base de enumeración de:AllMyColours
- Y entonces un poder sub -class todos los colores a:PrimaryAndPastelColours
y, posteriormente, sub -class esto:PrimaryColours
(manteniendo la jerarquía en cuenta). Sin embargo, Java tampoco lo permitirá.Respuestas:
No, no puedes hacer esto en Java. Aparte de cualquier otra cosa,
d
presumiblemente sería una instancia deA
(dada la idea normal de "extensiones"), pero los usuarios que solo sabían sobreA
esto no lo sabrían, lo que frustra el punto de que un enum sea un conjunto bien conocido de valores.Si pudiera contarnos más acerca de cómo desea usar esto, podríamos sugerir soluciones alternativas.
fuente
Object->A->B
lugar deObject->A->B extends Object
Las enumeraciones representan una enumeración completa de los posibles valores. Entonces la respuesta (inútil) es no.
Como ejemplo de un problema real, tome los días de semana, los días de fin de semana y, la unión, los días de la semana. Podríamos definir todos los días dentro de los días de la semana, pero no podríamos representar propiedades especiales para los días de la semana y los días de fin de semana.
Lo que podríamos hacer es tener tres tipos de enumeración con un mapeo entre días de la semana / días de fin de semana y días de la semana.
Alternativamente, podríamos tener una interfaz abierta para el día de la semana:
O podríamos combinar los dos enfoques:
fuente
La solución recomendada para esto es el patrón de enumeración extensible .
Esto implica crear una interfaz y usarla donde actualmente usa la enumeración. Luego haga que la enumeración implemente la interfaz. Puede agregar más constantes haciendo que esa nueva enumeración también extienda la interfaz.
fuente
Debajo de las cubiertas, su ENUM es solo una clase regular generada por el compilador. Esa clase generada se extiende
java.lang.Enum
. La razón técnica por la que no puede extender la clase generada es que la clase generada esfinal
. Las razones conceptuales para que sea final se discuten en este tema. Pero agregaré la mecánica a la discusión.Aquí hay una enumeración de prueba:
El código resultante de javap:
Posiblemente podría escribir esta clase por su cuenta y soltar el "final". Pero el compilador le impide extender "java.lang.Enum" directamente. Podrías decidir NO extender java.lang.Enum, pero entonces tu clase y sus clases derivadas no serían una instancia de java.lang.Enum ... ¡lo cual realmente no te importaría de ninguna manera!
fuente
Se puede escribir como:
Cómo puede ser útil: Digamos que queremos algo como: Tenemos eventos y estamos usando enumeraciones. Esas enumeraciones se pueden agrupar por procesamiento similar. Si tenemos operación con muchos elementos, algunos eventos comienzan a funcionar, algunos son solo pasos y otros finalizan la operación. Para recopilar dicha operación y evitar un caso de cambio largo, podemos agruparlos como en el ejemplo y usar:
Ejemplo:
Agregue algunos más avanzados:
En el caso anterior, si tenemos algún error (myEvent.is (State_StatusGroup.FAIL)), iterando por eventos anteriores podemos verificar fácilmente si debemos revertir la transferencia de dinero:
Puede ser útil para:
fuente
Aquí hay una forma en que descubrí cómo extender una enumeración a otra enumeración, es un enfoque muy directo:
Supongamos que tienes una enumeración con constantes comunes:
entonces puedes intentar hacer un manual extendido de esta manera:
por supuesto, cada vez que necesite extender una constante, debe modificar sus archivos SubEnum.
fuente
En caso de que te lo hayas perdido, hay un capítulo en el excelente libro de Joshua Bloch " Java Effective, 2nd edition ".
Extraer aquí .
Solo la conclusión:
fuente
Tiendo a evitar las enumeraciones, porque no son extensibles. Para seguir con el ejemplo del OP, si A está en una biblioteca y B en su propio código, no puede extender A si es una enumeración. Así es como a veces reemplazo las enumeraciones:
Hay algunos hoyos para evitar, vea los comentarios en el código. Dependiendo de sus necesidades, esta es una alternativa sólida y extensible a las enumeraciones.
fuente
Así es como mejoro el patrón de herencia enum con la verificación de tiempo de ejecución en el inicializador estático. Las
BaseKind#checkEnumExtender
comprobaciones de que la "extensión" enum declara todos los valores de la base enum exactamente de la misma manera#name()
y#ordinal()
siguen siendo totalmente compatibles.Todavía hay que copiar y pegar para declarar valores, pero el programa falla rápidamente si alguien agrega o modifica un valor en la clase base sin actualizar los que se extienden.
Comportamiento común para diferentes enumeraciones que se extienden entre sí:
Base enum, con método de verificación:
Muestra de extensión:
fuente
Basado en @Tom Hawtin - respuesta tackline agregamos soporte de interruptor,
fuente
valueOf()
método?Day
que tiene el métodovalueOf()
a continuaciónswitch(Day.valueOf())
, se implementa medianteWeekDay, WeekEndDay
enumeraciones.Le sugiero que tome el enfoque inverso.
En lugar de extender la enumeración existente, cree una más grande y cree un subconjunto de la misma. Por ejemplo, si tenía una enumeración llamada PET y desea extenderla a ANIMAL, debe hacer esto en su lugar:
Tenga cuidado, las mascotas no son colecciones inmutables, es posible que desee utilizar Guava o Java9 para mayor seguridad.
fuente
Habiendo tenido este mismo problema, me gustaría publicar mi perspectiva. Creo que hay un par de factores motivadores para hacer algo como esto:
El uso de una interfaz realmente no es suficiente: accidentalmente puede obtener valores de enumeración duplicados. No deseable.
Terminé simplemente combinando las enumeraciones: esto garantiza que no pueda haber valores duplicados, a expensas de estar menos estrechamente vinculado a su clase asociada. Pero, pensé que el problema duplicado era mi principal preocupación ...
fuente
Como ayuda para comprender por qué extender una Enum no es razonable en el nivel de implementación del lenguaje para considerar lo que sucedería si pasara una instancia de la Enum extendida a una rutina que solo comprende la Enum base. Un cambio que el compilador prometió tenía todos los casos cubiertos, de hecho, no cubriría esos valores extendidos de Enum.
Esto enfatiza aún más que los valores de Java Enum no son enteros como los de C, por ejemplo: para usar un Java Enum como un índice de matriz, debe solicitar explícitamente su miembro ordinal (), para darle a un Java Enum un valor entero arbitrario que debe agregar un campo explícito para eso y referencia a ese miembro nombrado.
Este no es un comentario sobre el deseo del OP, solo sobre por qué Java nunca lo va a hacer.
fuente
Con la esperanza de que esta elegante solución de un colega mío se vea incluso en esta larga publicación, me gustaría compartir este enfoque para la subclasificación que sigue el enfoque de la interfaz y más allá.
Tenga en cuenta que aquí utilizamos excepciones personalizadas y este código no se compilará a menos que lo reemplace con sus excepciones.
La documentación es extensa y espero que sea comprensible para la mayoría de ustedes.
La interfaz que cada enumeración subclase necesita implementar.
La implementación de la clase base ENUM.
El ENUM subclasificado que "hereda" de la clase base.
Finalmente el genérico ParameterImpl para agregar algunas utilidades.
fuente
Mi forma de codificar sería la siguiente:
LinkedHashSet
proporciona tanto que cada entrada solo existe una vez, y que su orden se conserva. Si el orden no importa, puedes usarloHashSet
en su lugar. El siguiente código no es posible en Java:El código se puede escribir de la siguiente manera:
Desde Java 7 en adelante, incluso puede hacer lo mismo con
String
:Usando el reemplazo de enumeración:
fuente