¿Se pueden subescribir los enums para agregar nuevos elementos?

Quiero tomar una enumeración existente y agregarle más elementos de la siguiente manera:

enum A {a,b,c} enum B extends A {d} /*B is {a,b,c,d}*/ 

¿Es esto posible en Java?

No, no puedes hacer esto en Java. Aparte de cualquier otra cosa, d sería presumiblemente una instancia de A (dada la idea normal de “extender”), pero los usuarios que solo sabían acerca de A no lo sabrían, lo que frustra el punto de que una enumeración sea un pozo. conjunto conocido de valores.

Si pudiera contarnos más acerca de cómo desea utilizar esto, podríamos sugerir soluciones alternativas.

Los enumeraciones representan una enumeración completa de valores posibles. Entonces la respuesta (inútil) es no.

Como ejemplo de un problema real, tome los días de la semana, los fines de semana y la unión, los días de la semana. Podríamos definir todos los días en días de la semana, pero luego no podríamos representar propiedades especiales para los días de la semana y los fines 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.

 public enum Weekday { MON, TUE, WED, THU, FRI; public DayOfWeek toDayOfWeek() { ... } } public enum WeekendDay { SAT, SUN; public DayOfWeek toDayOfWeek() { ... } } public enum DayOfWeek { MON, TUE, WED, THU, FRI, SAT, SUN; } 

Alternativamente, podríamos tener una interfaz abierta para el día de la semana:

 interface Day { ... } public enum Weekday implements Day { MON, TUE, WED, THU, FRI; } public enum WeekendDay implements Day { SAT, SUN; } 

O podríamos combinar los dos enfoques:

 interface Day { ... } public enum Weekday implements Day { MON, TUE, WED, THU, FRI; public DayOfWeek toDayOfWeek() { ... } } public enum WeekendDay implements Day { SAT, SUN; public DayOfWeek toDayOfWeek() { ... } } public enum DayOfWeek { MON, TUE, WED, THU, FRI, SAT, SUN; public Day toDay() { ... } } 

La solución recomendada para esto es el patrón de enumeración extensible .

Esto implica crear una interfaz y usarla en el lugar donde usa actualmente la enumeración. Luego haz que la enumeración implemente la interfaz. Puede agregar más constantes haciendo que la nueva enumeración también amplíe la interfaz.

Bajo las cubiertas, su ENUM es solo una clase normal generada por el comstackdor. Esa clase generada extiende java.lang.Enum . La razón técnica por la que no se puede extender la clase generada es que la clase generada es final . Las razones conceptuales para que sea final se tratan en este tema. Pero agregaré los mecanismos a la discusión.

Aquí hay una enumeración de prueba:

 public enum TEST { ONE, TWO, THREE; } 

El código resultante de javap:

 public final class TEST extends java.lang.Enum { public static final TEST ONE; public static final TEST TWO; public static final TEST THREE; static {}; public static TEST[] values(); public static TEST valueOf(java.lang.String); } 

Posiblemente podría escribir esta clase por su cuenta y soltar el “final”. Pero el comstackdor 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 que realmente no te importaría de ninguna manera!

 enum A {a,b,c} enum B extends A {d} /*B is {a,b,c,d}*/ 

Se puede escribir como:

 public enum All { 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 enums. Esas enumeraciones se pueden agrupar por un procesamiento similar. Si tenemos operaciones con muchos elementos, algunos eventos comienzan a funcionar, algunos son solo pasos y otros finalizan la operación. Para recostackr dicha operación y evitar una larga caja de conmutadores, podemos agruparlos como en el ejemplo y usar:

 if(myEvent.is(State_StatusGroup.START)) makeNewOperationObject().. if(myEnum.is(State_StatusGroup.STEP)) makeSomeSeriousChanges().. if(myEnum.is(State_StatusGroup.FINISH)) closeTransactionOrSomething().. 

Ejemplo:

 public enum AtmOperationStatus { STARTED_BY_SERVER (State_StatusGroup.START), SUCCESS (State_StatusGroup.FINISH), FAIL_TOKEN_TIMEOUT (State_StatusGroup.FAIL, State_StatusGroup.FINISH), FAIL_NOT_COMPLETE (State_StatusGroup.FAIL, State_StatusGroup.STEP), FAIL_UNKNOWN (State_StatusGroup.FAIL, State_StatusGroup.FINISH), (...) private AtmOperationStatus(StatusGroupInterface ... pList){ for (StatusGroupInterface group : pList){ group.addMember(this); } } public boolean is(StatusGroupInterface with){ for (AtmOperationStatus eT : with.getMembers()){ if( eT .equals(this)) return true; } return false; } // Each group must implement this interface private interface StatusGroupInterface{ EnumSet getMembers(); void addMember(AtmOperationStatus pE); } // DEFINING GROUPS public enum State_StatusGroup implements StatusGroupInterface{ START, STEP, FAIL, FINISH; private List members = new LinkedList(); @Override public EnumSet getMembers() { return EnumSet.copyOf(members); } @Override public void addMember(AtmOperationStatus pE) { members.add(pE); } static { // forcing initiation of dependent enum try { Class.forName(AtmOperationStatus.class.getName()); } catch (ClassNotFoundException ex) { throw new RuntimeException("Class AtmEventType not found", ex); } } } } //Some use of upper code: if (p.getStatus().is(AtmOperationStatus.State_StatusGroup.FINISH)) { //do something }else if (p.getStatus().is(AtmOperationStatus.State_StatusGroup.START)) { //do something } 

Agregue algunos más avanzados:

 public enum AtmEventType { USER_DEPOSIT (Status_EventsGroup.WITH_STATUS, Authorization_EventsGroup.USER_AUTHORIZED, ChangedMoneyAccountState_EventsGroup.CHANGED, OperationType_EventsGroup.DEPOSIT, ApplyTo_EventsGroup.CHANNEL), SERVICE_DEPOSIT (Status_EventsGroup.WITH_STATUS, Authorization_EventsGroup.TERMINAL_AUTHORIZATION, ChangedMoneyAccountState_EventsGroup.CHANGED, OperationType_EventsGroup.DEPOSIT, ApplyTo_EventsGroup.CHANNEL), DEVICE_MALFUNCTION (Status_EventsGroup.WITHOUT_STATUS, Authorization_EventsGroup.TERMINAL_AUTHORIZATION, ChangedMoneyAccountState_EventsGroup.DID_NOT_CHANGED, ApplyTo_EventsGroup.DEVICE), CONFIGURATION_4_C_CHANGED(Status_EventsGroup.WITHOUT_STATUS, ApplyTo_EventsGroup.TERMINAL, ChangedMoneyAccountState_EventsGroup.DID_NOT_CHANGED), (...) 

En la parte superior, si fallamos un poco (myEvent.is (State_StatusGroup.FAIL)) y luego iteramos por eventos anteriores, podemos verificar fácilmente si debemos revertir la transferencia de dinero mediante:

 if(myEvent2.is(ChangedMoneyAccountState_EventsGroup.CHANGED)) rollBack().. 

Puede ser útil para:

  1. incluyendo metadatos explicitados sobre lógica de procesamiento, menos para recordar
  2. implementando algunas de multi-herencia
  3. no queremos usar estructuras de clase, ej. para enviar mensajes cortos de estado

Aquí hay una manera de cómo encontré cómo extender una enumeración en otra enumeración, es un enfoque muy directo:

Suposse que tiene una enumeración con constantes comunes:

 public interface ICommonInterface { String getName(); } public enum CommonEnum implements ICommonInterface { P_EDITABLE("editable"), P_ACTIVE("active"), P_ID("id"); private final String name; EnumCriteriaComun(String name) { name= name; } @Override public String getName() { return this.name; } } 

entonces puedes intentar hacer un manual que se extiende de esta manera:

 public enum SubEnum implements ICommonInterface { P_EDITABLE(CommonEnum.P_EDITABLE ), P_ACTIVE(CommonEnum.P_ACTIVE), P_ID(CommonEnum.P_ID), P_NEW_CONSTANT("new_constant"); private final String name; EnumCriteriaComun(CommonEnum commonEnum) { name= commonEnum.name; } EnumCriteriaComun(String name) { name= name; } @Override public String getName() { return this.name; } } 

por supuesto, cada vez que necesita extender una constante, debe modificar sus archivos SubEnum.

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 – Enumeraciones y anotaciones
    • Ítem ​​34: emular enums extensibles con interfaces

Extracto aquí .

Solo la conclusión:

Una pequeña desventaja del uso de interfaces para emular enumeraciones extensibles es que las implementaciones no se pueden heredar 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 Operación Básica y Operación Extendida. 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 de ayuda o un método de ayuda 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 para ir con un tipo de enumeración básica que implementa la interfaz. Esto permite a los clientes escribir sus propias enumeraciones que implementan la interfaz. Estas enumeraciones se pueden usar en cualquier lugar donde se pueda usar el tipo de enumeración básico, suponiendo que las API se escriben en términos de la interfaz.

Tiendo a evitar enums, porque no son extensibles. Para permanecer 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 algunas veces sustituyo las enumeraciones:

 // access like enum: Aa public class A { public static final A a = new A(); public static final A b = new A(); public static final 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. */ public static int maxId=0; public int id = maxId++; public int getId() { return id; } } public class B extends A { /* * good: you can do like * A x = getYourEnumFromSomeWhere(); * if(x instanceof B) ...; * to identify which enum x * is of. */ public static final A d = new A(); } public class 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. */ public static final A e = new A(); public int getId() { return -32489132; }; } 

Hay algunos pozos para evitar, vea los comentarios en el código. Dependiendo de tus necesidades, esta es una alternativa sólida y extensible a las enumeraciones.

Así es como realzo el patrón de herencia enum con la verificación de tiempo de ejecución en el inicializador estático. El BaseKind#checkEnumExtender verifica que la extensión “enum” declara todos los valores de la enumeración base exactamente de la misma manera, por lo que #name() y #ordinal() permanecen totalmente compatibles.

Todavía hay copiar y pegar involucrado para declarar valores, pero el progtwig falla rápidamente si alguien agregó o modificó un valor en la clase base sin actualizar los extendidos.

Comportamiento común para diferentes enumeraciones que se extienden entre sí:

 public interface Kind { /** * Let's say we want some additional member. */ String description() ; /** * Standard {@code Enum} method. */ String name() ; /** * Standard {@code Enum} method. */ int ordinal() ; } 

Enum base, con método de verificación:

 public enum BaseKind implements Kind { FIRST( "First" ), SECOND( "Second" ), ; private final String description ; public String description() { return description ; } private BaseKind( final String description ) { this.description = description ; } public static void checkEnumExtender( final Kind[] baseValues, final Kind[] extendingValues ) { if( extendingValues.length < baseValues.length ) { throw new IncorrectExtensionError( "Only " + extendingValues.length + " values against " + baseValues.length + " base values" ) ; } for( int i = 0 ; i < baseValues.length ; i ++ ) { final Kind baseValue = baseValues[ i ] ; final Kind extendingValue = extendingValues[ i ] ; if( baseValue.ordinal() != extendingValue.ordinal() ) { throw new IncorrectExtensionError( "Base ordinal " + baseValue.ordinal() + " doesn't match with " + extendingValue.ordinal() ) ; } if( ! baseValue.name().equals( extendingValue.name() ) ) { throw new IncorrectExtensionError( "Base name[ " + i + "] " + baseValue.name() + " doesn't match with " + extendingValue.name() ) ; } if( ! baseValue.description().equals( extendingValue.description() ) ) { throw new IncorrectExtensionError( "Description[ " + i + "] " + baseValue.description() + " doesn't match with " + extendingValue.description() ) ; } } } public static class IncorrectExtensionError extends Error { public IncorrectExtensionError( final String s ) { super( s ) ; } } } 

Muestra de extensión:

 public enum ExtendingKind implements Kind { FIRST( BaseKind.FIRST ), SECOND( BaseKind.SECOND ), THIRD( "Third" ), ; private final String description ; public String description() { return description ; } ExtendingKind( final BaseKind baseKind ) { this.description = baseKind.description() ; } ExtendingKind( final String description ) { this.description = description ; } } 

Habiendo tenido este mismo problema yo mismo, me gustaría publicar mi perspectiva. Creo que hay un par de factores motivadores para hacer algo como esto:

  • Desea tener algunos códigos enum relacionados, pero en diferentes clases. En mi caso, tuve una clase base con varios códigos definidos en una enumeración asociada. En una fecha posterior (¡hoy!), Quería proporcionar algunas funcionalidades nuevas a la clase base, lo que también significaba nuevos códigos para la enumeración.
  • La clase derivada soportaría tanto la enumeración de las clases base como la suya propia. ¡No hay valores enum duplicados! Entonces: cómo tener una enumeración para la subclase que incluye la enumeración de su padre junto con sus nuevos valores.

Usar una interfaz realmente no lo corta: accidentalmente puede obtener valores enum duplicados. No deseable.

Terminé simplemente combinando las enumeraciones: esto asegura que no puede haber ningún valor duplicado, a expensas de estar menos estrechamente vinculado a su clase asociada. Pero, pensé que el problema duplicado era mi principal preocupación …

Te sugiero que te desvíes del enfoque.

En lugar de extender la enumeración existente, cree una más grande y cree un subconjunto de la misma. Por ejemplo, si tiene una enumeración llamada PET y desea extenderla a ANIMAL, debe hacer esto en su lugar:

 public enum ANIMAL { WOLF,CAT, DOG } EnumSet 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.

Como una ayuda para entender por qué la extensión de un Enum no es razonable en el nivel de implementación del lenguaje para considerar qué pasaría si pasara una instancia del Enum extendido a una rutina que solo entienda la base Enum. Un cambio que el comstackdor prometió cubrió todos los casos, de hecho, no cubriría esos valores Enum extendidos.

Esto enfatiza aún más que los valores de Java Enum no son enteros, como los de C, por ejemplo: para usar un Enum de Java como un índice de matriz, debe solicitar explícitamente su miembro ordinal (), para dar a un Enum en Java un valor entero arbitrario que debe agregar un campo explícito para eso y referencia ese miembro nombrado.

Esto no es un comentario sobre el deseo del OP, solo sobre por qué Java nunca va a hacerlo.

Basado en @Tom Hawtin – respuesta de tachado , agregamos soporte de interruptor,

 interface Day { ... T valueOf(); } public enum Weekday implements Day { MON, TUE, WED, THU, FRI; Weekday valueOf(){ return valueOf(name()); } } public enum WeekendDay implements Day { SAT, SUN; WeekendDay valueOf(){ return valueOf(name()); } } Day wds = Weekday.MON; Day wends = WeekendDay.SUN; switch(wds.valueOf()){ case MON: case TUE: case WED: case THU: case FRI: } switch(wends.valueOf()){ case SAT: case SUN: } 

Con la esperanza de que esta elegante solución de un colega mío se haya visto en esta larga publicación, me gustaría compartir este enfoque para la creación de subclases que sigue el enfoque de la interfaz y más allá.

Tenga en cuenta que usamos excepciones personalizadas aquí y este código no se comstackrá 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 enum subclasificada necesita implementar.

 public interface Parameter { /** * 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 optional - the string to check for * @return true in case this parameter has no pattern defined or the given string * matches the defined one, false in case valueStr is * null or an existing pattern is not matched */ boolean match(final String valueStr); /** * This method works as {@link #match(String)} but throws an exception if not matched. * * @param valueStr optional - the string to check for * @throws ArgumentException with code * 
*
PARAM_MISSED
*
if valueStr is null
*
PARAM_BAD
*
if pattern is not matched
*
*/ void matchEx(final String valueStr) throws ArgumentException; /** * 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 (eg a pattern). * * @param valueStr optional - the string to parse the parameter value from * @return the parameter value according to the parameters type (see {@link #getType()}) or * null in case valueStr was null. * @throws ArgumentException in case valueStr is not parsable as a value for this * parameter. */ Object parse(final String valueStr) throws ArgumentException; /** * 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 (eg for * enumerations), this method has to be implemented accordingly. * * @param value mandatory - the parameters value * @return the external form of the parameters value, never null * @throws InternalServiceException in case the given value does not match * {@link #getType()} */ String toString(final Object value) throws InternalServiceException; }

La implementación de la clase base ENUM.

 public enum Parameters implements Parameter { /** * ANY ENUM VALUE */ VALUE(new ParameterImpl("VALUE", String.class, "[A-Za-z]{3,10}")); /** * The parameter wrapped by this enum constant. */ private Parameter param; /** * Constructor. * * @param param mandatory - the value for {@link #param} */ private Parameters(final Parameter param) { this.param = param; } /** * {@inheritDoc} */ @Override public String getName() { return this.param.getName(); } /** * {@inheritDoc} */ @Override public Class getType() { return this.param.getType(); } /** * {@inheritDoc} */ @Override public boolean match(final String valueStr) { return this.param.match(valueStr); } /** * {@inheritDoc} */ @Override public void matchEx(final String valueStr) { this.param.matchEx(valueStr); } /** * {@inheritDoc} */ @Override public Object parse(final String valueStr) throws ArgumentException { return this.param.parse(valueStr); } /** * {@inheritDoc} */ @Override public String toString(final Object value) throws InternalServiceException { return this.param.toString(value); } } 

El ENUM subclasificado que “hereda” de la clase base.

 public enum ExtendedParameters implements Parameter { /** * ANY ENUM VALUE */ VALUE(my.package.name.VALUE); /** * EXTENDED ENUM VALUE */ EXTENDED_VALUE(new ParameterImpl("EXTENDED_VALUE", String.class, "[0-9A-Za-z_.-]{1,20}")); /** * The parameter wrapped by this enum constant. */ private Parameter param; /** * Constructor. * * @param param mandatory - the value for {@link #param} */ private Parameters(final Parameter param) { this.param = param; } /** * {@inheritDoc} */ @Override public String getName() { return this.param.getName(); } /** * {@inheritDoc} */ @Override public Class getType() { return this.param.getType(); } /** * {@inheritDoc} */ @Override public boolean match(final String valueStr) { return this.param.match(valueStr); } /** * {@inheritDoc} */ @Override public void matchEx(final String valueStr) { this.param.matchEx(valueStr); } /** * {@inheritDoc} */ @Override public Object parse(final String valueStr) throws ArgumentException { return this.param.parse(valueStr); } /** * {@inheritDoc} */ @Override public String toString(final Object value) throws InternalServiceException { return this.param.toString(value); } } 

Finalmente, el parámetro genérico Parameter agrega algunas utilidades.

 public class ParameterImpl implements Parameter { /** * The default pattern for numeric (integer, long) parameters. */ private static final Pattern NUMBER_PATTERN = Pattern.compile("[0-9]+"); /** * The default pattern for parameters of type boolean. */ private static final Pattern BOOLEAN_PATTERN = Pattern.compile("0|1|true|false"); /** * The name of the parameter, never null. */ private final String name; /** * The data type of the parameter. */ private final Class type; /** * The validation pattern for the parameters values. This may be null. */ private final Pattern validator; /** * Shortcut constructor without validatorPattern. * * @param name mandatory - the value for {@link #name} * @param type mandatory - the value for {@link #type} */ public ParameterImpl(final String name, final Class type) { this(name, type, null); } /** * Constructor. * * @param name mandatory - the value for {@link #name} * @param type mandatory - the value for {@link #type} * @param validatorPattern - optional - the pattern for {@link #validator} * 
*
Note: *
The default validation patterns {@link #NUMBER_PATTERN} or * {@link #BOOLEAN_PATTERN} are applied accordingly. *
*/ public ParameterImpl(final String name, final Class
type, final String validatorPattern) { this.name = name; this.type = type; if (null != validatorPattern) { this.validator = Pattern.compile(validatorPattern); } else if (Integer.class == this.type || Long.class == this.type) { this.validator = NUMBER_PATTERN; } else if (Boolean.class == this.type) { this.validator = BOOLEAN_PATTERN; } else { this.validator = null; } } /** * {@inheritDoc} */ @Override public boolean match(final String valueStr) { if (null == valueStr) { return false; } if (null != this.validator) { final Matcher matcher = this.validator.matcher(valueStr); return matcher.matches(); } return true; } /** * {@inheritDoc} */ @Override public void matchEx(final String valueStr) throws ArgumentException { if (false == this.match(valueStr)) { if (null == valueStr) { throw ArgumentException.createEx(ErrorCode.PARAM_MISSED, "The value must not be null", this.name); } throw ArgumentException.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 optional - the string value to parse the value from * @return the parsed value, may be null * @throws ArgumentException in case the parameter: *
    *
  • does not {@link #matchEx(String)} the {@link #validator}
  • *
  • cannot be parsed according to {@link #type}
  • *
* @throws InternalServiceException in case the type {@link #type} cannot be handled. This is a * programming error. */ @Override public T parse(final String valueStr) throws ArgumentException, InternalServiceException { if (null == valueStr) { return null; } this.matchEx(valueStr); if (String.class == this.type) { return this.type.cast(valueStr); } if (Boolean.class == this.type) { return this.type.cast(Boolean.valueOf(("1".equals(valueStr)) || Boolean.valueOf(valueStr))); } try { if (Integer.class == this.type) { return this.type.cast(Integer.valueOf(valueStr)); } if (Long.class == this.type) { return this.type.cast(Long.valueOf(valueStr)); } } catch (final NumberFormatException e) { throw ArgumentException.createEx(ErrorCode.PARAM_BAD, "The value cannot be parsed as " + this.type.getSimpleName().toLowerCase() + ".", this.name); } return this.parseOther(valueStr); } /** * Field access for {@link #name}. * * @return the value of {@link #name}. */ @Override public String getName() { return this.name; } /** * Field access for {@link #type}. * * @return the value of {@link #type}. */ @Override public Class
getType() { return this.type; } /** * {@inheritDoc} */ @Override public final String toString(final Object value) throws InternalServiceException { if (false == this.type.isAssignableFrom(value.getClass())) { throw new InternalServiceException(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) { return String.valueOf(value); } if (Boolean.class == this.type) { return Boolean.TRUE.equals(value) ? "1" : "0"; } return this.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. *
*
Note: *
This default implementation always throws an InternalServiceException. *
* * @param valueStr mandatory - the string value to parse the value from * @return the parsed value, may be null * @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(final String valueStr) throws ArgumentException, InternalServiceException { throw new InternalServiceException(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. *
*
Note: *
This default implementation always throws an InternalServiceException. *
* * @param value mandatory - the parameters value * @return the external form of the parameters value, never null * @throws InternalServiceException in case the given value does not match * {@link #getClass()} */ protected String toStringOther(final Object value) throws InternalServiceException { throw new InternalServiceException(ErrorCode.PANIC, "ParameterImpl.toStringOther(): Unsupported parameter type: " + this.type.getName()); } }

My way to code that would be as follows:

 // enum A { a, b, c } static final Set enumA = new LinkedHashSet<>(Arrays.asList(new Short[]{'a','b','c'})); // enum B extends A { d } static final Set enumB = new LinkedHashSet<>(enumA); static { enumB.add((short) 'd'); // If you have to add more elements: // enumB.addAll(Arrays.asList(new Short[]{ 'e', 'f', 'g', '♯', '♭' })); } 

LinkedHashSet provides both that each entry only exists once, and that their order is preserved. If order doesn’t matter, you can use HashSet instead. The following code is not possible in 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: throw new IllegalStateException("This should never happen."); } } 

The code can be written as follows:

 for (Short a : enumB) { switch (a) { case 'a': case 'b': case 'c': System.out.println("Value is: " + new String(Character.toChars(a))); break; default: throw new IllegalStateException("This should never happen."); } } 

From Java 7 onwards you can even do the same with String :

 // enum A { BACKWARDS, FOREWARDS, STANDING } static final Set enumA = new LinkedHashSet<>(Arrays.asList(new String[] { "BACKWARDS", "FOREWARDS", "STANDING" })); // enum B extends A { JUMP } static final Set enumB = new LinkedHashSet<>(enumA); static { enumB.add("JUMP"); } 

Using the enum replacement:

 for (String a : enumB) { switch (a) { case "BACKWARDS": case "FOREWARDS": case "STANDING": System.out.println("Value is: " + a); break; default: throw new IllegalStateException("This should never happen."); } }