¿Cuál es la ventaja de una enumeración Java frente a una clase con campos finales públicos estáticos?

Estoy muy familiarizado con C # pero comencé a trabajar más en Java. Esperaba aprender que las enumeraciones en Java eran básicamente equivalentes a las de C #, pero aparentemente este no es el caso. Inicialmente, me entusiasmó saber que las enumeraciones de Java podían contener múltiples datos que parecen muy ventajosos ( http://docs.oracle.com/javase/tutorial/java/javaOO/enum.html ). Sin embargo, desde entonces he encontrado que faltan muchas características que son triviales en C #, como la capacidad de asignar fácilmente un determinado valor a un elemento enum y, en consecuencia, la capacidad de convertir un entero en una enumeración sin una cantidad decente de esfuerzo ( es decir, convertir el valor entero para hacer coincidir Java Enum ).

Así que mi pregunta es esta: ¿hay algún beneficio para las enumeraciones de Java sobre una clase con un montón de campos finales públicos estáticos? ¿O solo proporciona una syntax más compacta?

EDITAR: Déjame ser más claro. ¿Cuál es el beneficio de las enumeraciones Java sobre una clase con un montón de campos finales públicos estáticos del mismo tipo ? Por ejemplo, en el ejemplo de planetas en el primer enlace, ¿cuál es la ventaja de una enumeración sobre una clase con estas constantes públicas?

public static final Planet MERCURY = new Planet(3.303e+23, 2.4397e6); public static final Planet VENUS = new Planet(4.869e+24, 6.0518e6); public static final Planet EARTH = new Planet(5.976e+24, 6.37814e6); public static final Planet MARS = new Planet(6.421e+23, 3.3972e6); public static final Planet JUPITER = new Planet(1.9e+27, 7.1492e7); public static final Planet SATURN = new Planet(5.688e+26, 6.0268e7); public static final Planet URANUS = new Planet(8.686e+25, 2.5559e7); public static final Planet NEPTUNE = new Planet(1.024e+26, 2.4746e7); 

Por lo que puedo decir, la respuesta de casablanca es la única que satisface esto.

Técnicamente, uno podría ver las enumeraciones como una clase con un montón de constantes tipadas, y de hecho así es como las constantes enum se implementan internamente. Sin embargo, el uso de una enum le brinda métodos útiles ( Enum javadoc ) que de otra manera tendría que implementar usted mismo, como Enum.valueOf .

  1. Escriba seguridad y valore la seguridad.
  2. Singleton garantizado
  3. Capacidad para definir y anular métodos.
  4. Posibilidad de usar valores en declaraciones de case y case sin calificación.
  5. Secuenciación de valores incorporada vía ordinal().
  6. Serialización por nombre, no por valor, que ofrece un grado de protección contra el futuro.
  7. EnumSet y EnumMap .

Nadie mencionó la posibilidad de usarlos en declaraciones de switch ; Voy a arrojar eso también.

Esto permite que las enumeraciones arbitrariamente complejas se utilicen de forma limpia sin utilizar instanceof , lo que puede confundir las secuencias o los valores de conmutación no de cadena / int. El ejemplo canónico es una máquina de estado.

Hay menos confusión Tome Font por ejemplo. Tiene un constructor que toma el nombre de la Font que desea, su tamaño y su estilo ( new Font(String, int, int) ). Hasta el día de hoy no recuerdo si el estilo o el tamaño van primero. Si Font hubiera usado una enum para todos sus diferentes estilos ( PLAIN , BOLD , ITALIC , BOLD_ITALIC ), su constructor se vería como Font(String, Style, int) , evitando cualquier confusión. Desafortunadamente, las enum s no estaban disponibles cuando se creó la clase de Font , y como Java tiene que mantener la compatibilidad inversa, siempre estaremos atormentados por esta ambigüedad.

Por supuesto, esto es solo un argumento para usar una enum lugar de constantes public static final . Las entradas también son perfectas para singletons e implementan comportamientos predeterminados a la vez que permiten una personalización posterior (es decir, el patrón de estrategia ). Un ejemplo de este último es java.nio.file de OpenOption y StandardOpenOption : si un desarrollador quería crear su propia OpenOption no estándar, podría OpenOption .

La principal ventaja es la seguridad del tipo. Con un conjunto de constantes, cualquier valor del mismo tipo intrínseco podría ser utilizado, introduciendo errores. Con una enumeración, solo se pueden usar los valores aplicables.

Por ejemplo

 public static final int SIZE_SMALL = 1; public static final int SIZE_MEDIUM = 2; public static final int SIZE_LARGE = 3; public void setSize(int newSize) { ... } obj.setSize(15); // Compiles but likely to fail later 

vs

 public enum Size { SMALL, MEDIUM, LARGE }; public void setSize(Size s) { ... } obj.setSize( ? ); // Can't even express the above example with an enum 

Aquí hay muchas buenas respuestas, pero ninguna menciona que haya implementaciones altamente optimizadas de las clases / interfaces de Collection API específicamente para las enumeraciones :

  • EnumSet
  • EnumMap

Estas clases específicas de enum solo aceptan instancias de Enum (los EnumMap solo aceptan Enum s solo como claves) y, siempre que sea posible, vuelven a la representación compacta y la manipulación de bits en su implementación.

¿Qué significa esto?

Si nuestro tipo Enum no tiene más de 64 elementos (la mayoría de los ejemplos de Enum vida real calificarán para esto), las implementaciones almacenan los elementos en un solo valor long , cada instancia Enum en cuestión se asociará con un poco de este 64- un poco long . Agregar un elemento a un EnumSet es simplemente establecer el bit apropiado en 1, eliminarlo es simplemente establecer ese bit en 0. ¡Comprobar si un elemento está en el Set es solo una prueba de máscara de bits! ¡Ahora debes amar a Enum para esto!

El primer beneficio de las enumeraciones, como ya habrás notado, es la simplicidad de la syntax. Pero el punto principal de las enumeraciones es proporcionar un conjunto bien conocido de constantes que, de forma predeterminada, forman un rango y ayudan a realizar un análisis de código más exhaustivo mediante comprobaciones de seguridad de tipo y valor.

Esos atributos de enumeraciones ayudan tanto a un progtwigdor como a un comstackdor. Por ejemplo, supongamos que ve una función que acepta un número entero. ¿Qué podría significar ese entero? ¿Qué tipo de valores puedes pasar? Realmente no sabes de inmediato. Pero si ve una función que acepta enum, conoce muy bien todos los valores posibles que puede pasar.

Para el comstackdor, las enumeraciones ayudan a determinar un rango de valores y, a menos que usted asigne valores especiales a los miembros enum, están bien rangos de 0 en adelante. Esto ayuda a rastrear automáticamente los errores en el código a través de controles de seguridad de tipo y más. Por ejemplo, el comstackdor puede advertirle que no maneja todos los valores de enum posibles en su statement de conmutación (es decir, cuando no tiene un caso default y maneja solo uno de los valores Numéricos). También te avisa cuando conviertes un entero arbitrario en enum porque el rango de valores de enum es menor que el entero y eso a su vez puede desencadenar errores en la función que realmente no acepta un número entero. Además, generar una tabla de salto para el cambio se vuelve más fácil cuando los valores son desde 0 y más.

Esto no solo es cierto para Java, sino también para otros lenguajes con una estricta comprobación de tipos. C, C ++, D, C # son buenos ejemplos.

ejemplo:

 public class CurrencyDenom { public static final int PENNY = 1; public static final int NICKLE = 5; public static final int DIME = 10; public static final int QUARTER = 25;} 

Limitación de Constantes de Java

1) Sin tipo de seguridad : en primer lugar, no es seguro para tipos; puede asignar cualquier valor int válido a int, por ej. 99, aunque no haya monedas para representar ese valor.

2) Sin impresión significativa : el valor de impresión de cualquiera de estas constantes imprimirá su valor numérico en lugar de un nombre significativo de moneda. Por ejemplo, al imprimir NICKLE, imprimirá “5” en lugar de “NICKLE”.

3) Sin espacio de nombres : para acceder a la constante currencyDenom necesitamos prefijar el nombre de la clase, por ejemplo, CurrencyDenom.PENNY en lugar de simplemente usar PENNY, aunque esto también se puede lograr usando la importación estática en JDK 1.5

Ventaja de enum

1) Los registros en Java son seguros para el tipo y tienen su propio espacio de nombres. Significa que su enumeración tendrá un tipo, por ejemplo, “Moneda” en el ejemplo siguiente y no podrá asignar ningún valor distinto a los especificados en Constantes de Enum.

 public enum Currency {PENNY, NICKLE, DIME, QUARTER}; 

Currency coin = Currency.PENNY; coin = 1; //comstacktion error

2) Enum en Java son tipo de referencia como clase o interfaz y puedes definir constructor, métodos y variables dentro de java Enum, lo que lo hace más poderoso que Enum en C y C ++ como se muestra en el siguiente ejemplo de tipo Java Enum.

3) Puede especificar valores de constantes enum en el momento de la creación como se muestra en el ejemplo siguiente: public enum Currency {PENNY (1), NICKLE (5), DIME (10), QUARTER (25)}; Pero para que esto funcione, debe definir una variable miembro y un constructor porque PENNY (1) está llamando realmente a un constructor que acepta el valor int, vea el ejemplo a continuación.

 public enum Currency { PENNY(1), NICKLE(5), DIME(10), QUARTER(25); private int value; private Currency(int value) { this.value = value; } 

};

refrence: http://javarevisited.blogspot.in/2011/08/enum-in-java-example-tutorial.html

enum Beneficios:

  1. Los enumerados son seguros para tipos, los campos estáticos no son
  2. Hay un número finito de valores (no es posible pasar el valor enum no existente. Si tiene campos de clase estáticos, puede cometer ese error)
  3. Cada enumeración puede tener múltiples propiedades (campos / captadores) asignadas – encapsulación. También algunos métodos simples: AÑO.aSegundos () o similar. Compare: Colors.toHex (Colores.RED) Colores.RED.getHex ()

“como la capacidad de asignar fácilmente un elemento enum a cierto valor”

 enum EnumX{ VAL_1(1), VAL_200(200); public final int certainValue; private X(int certainValue){this.certainValue = certainValue;} } 

“y, en consecuencia, la capacidad de convertir un entero a una enumeración sin una cantidad decente de esfuerzo”. Agregar un método de conversión de int a enum que hace eso. Simplemente agregue HashMap estático que contenga la asignación.

Si realmente quiere convertir ord = VAL_200.ordinal () de nuevo a val_200 simplemente use: EnumX.values ​​() [ord]

Una enumeración es implícitamente final, con constructores privados, todos sus valores son del mismo tipo o un subtipo, puede obtener todos sus valores usando values() , obtiene su valor de name() o ordinal() o puede mirar una enumeración por número o nombre.

También puedes definir subclases (aunque teóricamente definitivas, algo que no puedes hacer de otra manera)

 enum Runner implements Runnable { HI { public void run() { System.out.println("Hello"); } }, BYE { public void run() { System.out.println("Sayonara"); } public String toString() { return "good-bye"; } } } class MYRunner extends Runner // won't compile. 

Otra diferencia importante es que el comstackdor java trata static final campos static final de los tipos primitivos y String como literales. Significa que estas constantes se vuelven en línea. Es similar al preprocesador C/C++ #define . ver esta pregunta . Este no es el caso con enums.

Obtiene tiempo de comstackción para verificar valores válidos cuando usa una enumeración. Mira esta pregunta.

La mayor ventaja es que enum Singletons es fácil de escribir y seguro para subprocesos:

 public enum EasySingleton{ INSTANCE; } 

y

 /** * Singleton pattern example with Double checked Locking */ public class DoubleCheckedLockingSingleton{ private volatile DoubleCheckedLockingSingleton INSTANCE; private DoubleCheckedLockingSingleton(){} public DoubleCheckedLockingSingleton getInstance(){ if(INSTANCE == null){ synchronized(DoubleCheckedLockingSingleton.class){ //double checking Singleton instance if(INSTANCE == null){ INSTANCE = new DoubleCheckedLockingSingleton(); } } } return INSTANCE; } } 

ambos son similares y se encargó de la serialización por sí mismos al implementar

 //readResolve to prevent another instance of Singleton private Object readResolve(){ return INSTANCE; } 

Más

Creo que una enum no puede ser final , porque bajo el capó el comstackdor genera subclases para cada entrada enum .

Más información de fuente

Generalmente se considera una mala práctica. El problema es que las constantes son parte de la “interfaz” pública (a falta de una mejor palabra) de la clase implementadora. Esto significa que la clase de implementación está publicando todos estos valores en clases externas, incluso cuando solo se requieren internamente. Las constantes proliferan en todo el código. Un ejemplo es la interfaz SwingConstants en Swing, que se implementa mediante docenas de clases que todas “reexportan” todas sus constantes (incluso las que no usan) como propias.
El patrón de interfaz constante es un uso deficiente de las interfaces. Que una clase use algunas constantes internamente es un detalle de implementación. La implementación de una interfaz constante hace que este detalle de implementación se filtre en la API exportada de la clase. No tiene ninguna consecuencia para los usuarios de una clase que la clase implemente una interfaz constante. De hecho, incluso puede confundirlos. Peor aún, representa un compromiso: si en una versión futura la clase se modifica para que ya no necesite usar las constantes, todavía debe implementar la interfaz para garantizar la compatibilidad binaria. Si una clase no final implementa una interfaz constante, todas sus subclases tendrán sus espacios de nombres contaminados por las constantes en la interfaz.
Una enumeración puede ser un mejor enfoque. O simplemente podría poner las constantes como campos públicos estáticos en una clase que no se puede instanciar. Esto permite que otra clase acceda a ellos sin contaminar su propia API.