verificar descargar java

java - descargar - ¿Se pueden clasificar las enumeraciones para agregar nuevos elementos?



java offline (15)

Así es como mejoro el patrón de herencia de enumeración con el control de tiempo de ejecución en el inicializador estático. BaseKind#checkEnumExtender comprueba que la enumeración "extendida" declara todos los valores de la enumeración base exactamente de la misma manera, por lo que #name() y #ordinal() siguen siendo totalmente compatibles.

Todavía existe la función de 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 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() ; }

Enumeración 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 ) ; } } }

Ejemplo 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 ; } }

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?


Bajo las cubiertas, su ENUM es solo una clase regular generada por el compilador. Esa clase generada 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 voy a añadir la mecánica a la discusión.

Aquí hay una prueba de enumeración:

public enum TEST { ONE, TWO, THREE; }

El código resultante de javap:

public final class TEST extends java.lang.Enum<TEST> { 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 puedas escribir esta clase por tu cuenta y soltar la "final". Pero el compilador le impide extender "java.lang.Enum" directamente. Podría decidir NO extender java.lang.Enum, pero entonces su clase y sus clases derivadas no serían una instancia de java.lang.Enum ... ¡lo que realmente no le importaría de ninguna manera!


Como una ayuda para comprender por qué no es razonable extender un Enum 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 el Enum básico. Un interruptor que el compilador prometió tenía todos los casos cubiertos de hecho no cubrirá esos valores Enum extendidos.

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

Esto 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 este largo post, me gustaría compartir este enfoque para las subclases que siguen el enfoque de interfaz y más allá.

Tenga en cuenta que usamos excepciones personalizadas aquí y que 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 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 <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(final String 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(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 (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(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 (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(final Object value) throws InternalServiceException; }

La clase base implementadora ENUM.

public enum Parameters implements Parameter { /** * ANY ENUM VALUE */ VALUE(new ParameterImpl<String>("VALUE", String.class, "[A-Za-z]{3,10}")); /** * The parameter wrapped by this enum constant. */ private Parameter param; /** * Constructor. * * @param param <i>mandatory</i> - 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<String>("EXTENDED_VALUE", String.class, "[0-9A-Za-z_.-]{1,20}")); /** * The parameter wrapped by this enum constant. */ private Parameter param; /** * Constructor. * * @param param <i>mandatory</i> - 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 ParameterImpl genérico para agregar algunas utilidades.

public class ParameterImpl<T> 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 <code>null</code>. */ private final String name; /** * The data type of the parameter. */ private final Class<T> type; /** * The validation pattern for the parameters values. This may be <code>null</code>. */ private final Pattern 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} */ public ParameterImpl(final String name, final Class<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> */ public ParameterImpl(final String name, final Class<T> 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 <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. */ @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<T> 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. * <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(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. * <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()} */ protected String toStringOther(final Object value) throws InternalServiceException { throw new InternalServiceException(ErrorCode.PANIC, "ParameterImpl.toStringOther(): Unsupported parameter type: " + this.type.getName()); } }


En caso de que te lo perdieras, hay un capítulo en el excelente libro de Joshua Bloch " Java Effective, 2nd edition ".

  • Capítulo 6 - Enums y anotaciones
    • Ítem ​​34: Emular enumeraciones extensibles con interfaces

Extraer here .

Solo la conclusión:

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 otra. 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, si bien 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 estén escritas en términos de la interfaz.


Esta es una forma en la que encontré cómo extender una enumeración a otra enumeración, es un enfoque muy directo:

Supongamos que tienes 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 extienda 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 necesite extender una constante, tendrá que modificar sus archivos SubEnum.


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 algunas funciones 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 propia. No hay valores de enumeración duplicados! Entonces: cómo tener una enumeración para la subclase que incluye las enumas de su matriz junto con sus nuevos valores.

El uso de una interfaz realmente no lo corta: accidentalmente puede obtener valores de enumeración duplicados. No deseable.

Terminé simplemente combinando las enumeraciones: esto asegura que no puede haber valores duplicados, a costa de estar menos estrechamente ligados a su clase asociada. Pero, pensé que el problema duplicado era mi principal preocupación ...


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

Esto implica la creación de una interfaz y el uso de eso donde actualmente utiliza la enumeración. Luego haga que la enumeración implemente la interfaz. Puede agregar más constantes haciendo que la nueva enumeración también extienda la interfaz.


Las enumeraciones representan una enumeración completa de los valores posibles. Así que la respuesta (inútil) es no.

Como ejemplo de un problema real, tome los días de semana, los fines 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 luego no podríamos representar propiedades especiales para los días laborables y los fines de semana.

Lo que podríamos hacer es tener tres tipos de enumeración con una asignación entre días laborables / fines de semana-días 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() { ... } }


Le sugiero que tome el camino opuesto.

En lugar de ampliar 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 quería extenderla a ANIMAL, debería hacer esto en su lugar:

public enum ANIMAL { WOLF,CAT, DOG } EnumSet<ANIMAL> pets = EnumSet.of(ANIMAL.CAT, ANIMAL.DOG);

Tenga cuidado, las mascotas no son colecciones inmutables, puede usar Guava o Java9 para mayor seguridad.


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 "ampliaciones"), pero los usuarios que solo sabían acerca de A no sabrían de ella, lo que anula el punto de que una enumeración es una buena idea. Conjunto de valores conocidos.

Si pudiera decirnos más acerca de cómo desea usar esto, podríamos sugerirle soluciones alternativas.


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 sustituyo los enumerados:

// access like enum: A.a 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, ver los comentarios en el código. Dependiendo de sus necesidades, esta es una alternativa sólida y extensible a las enumeraciones.


Sobre la base de @Tom Hawtin - respuesta de tackline agregamos soporte de switch,

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


Mi forma de codificar sería la siguiente:

// enum A { a, b, c } static final Set<Short> enumA = new LinkedHashSet<>(Arrays.asList(new Short[]{''a'',''b'',''c''})); // enum B extends A { d } static final Set<Short> 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'', ''♯'', ''♭'' })); }

LinkedHashSetestablece que cada entrada solo existe una vez y que su orden se conserva. Si el orden no importa, puedes usar 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: throw new IllegalStateException("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: " + new String(Character.toChars(a))); break; default: throw new IllegalStateException("This should never happen."); } }

Desde Java 7 en adelante, incluso puedes hacer lo mismo con String:

// enum A { BACKWARDS, FOREWARDS, STANDING } static final Set<String> enumA = new LinkedHashSet<>(Arrays.asList(new String[] { "BACKWARDS", "FOREWARDS", "STANDING" })); // enum B extends A { JUMP } static final Set<String> enumB = new LinkedHashSet<>(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: throw new IllegalStateException("This should never happen."); } }


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 enumeraciones. Esos enums se pueden agrupar por procesamiento similar. Si tenemos operaciones con muchos elementos, entonces 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 interruptor largo, 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<AtmOperationStatus> getMembers(); void addMember(AtmOperationStatus pE); } // DEFINING GROUPS public enum State_StatusGroup implements StatusGroupInterface{ START, STEP, FAIL, FINISH; private List<AtmOperationStatus> members = new LinkedList<AtmOperationStatus>(); @Override public EnumSet<AtmOperationStatus> 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 un poco más avanzado:

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 tenemos algunos fallos (myEvent.is (State_StatusGroup.FAIL)) y luego iterando 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 explícitos sobre la lógica de procesamiento, menos para recordar.
  2. la implementación de algunos de herencia múltiple
  3. no queremos usar estructuras de clase, ej. para enviar mensajes cortos de estado