Un conjunto fijo de valores con nombre
Algunos valores solo tienen sentido como uno de una lista pequeña y conocida: los días de la semana, los palos de una baraja, los estados en los que puede estar un pedido. Podrías modelarlos con códigos int (0 para pendiente, 1 para enviado) o con cadenas ("PENDING", "SHIPPED"), pero nada impide que quien llama pase 7 o "PNEDING". Un enum cierra ese agujero: el propio tipo solo admite los valores que has declarado.
Day es un tipo completamente nuevo. Una variable de tipo Day puede contener exactamente una de esas siete constantes (o null) y nada más. El compilador rechaza Day d = "WEDNESDAY"; o Day d = 2;. Esa garantía es justo el objetivo.
Cada constante (Day.MONDAY, etc.) es una única instancia compartida, creada una sola vez. Como solo existe un MONDAY, comparas enums con == en lugar de equals: son el mismo objeto.
Usar switch sobre un enum
Los enums encajan de forma natural con switch. Ni siquiera necesitas cualificar cada constante con el nombre del tipo dentro del switch:
Dentro del switch, escribe case SATURDAY, no case Day.SATURDAY: Java infiere el tipo. La gran ventaja frente a usar switch sobre una cadena o un int es que si añades una nueva constante más adelante, tu IDE y las comprobaciones de exhaustividad pueden señalar cada switch que se olvidó de gestionarla.
Recorrer todas las constantes
Cada enum obtiene automáticamente un método estático values() que devuelve todas sus constantes en el orden de declaración, además de un ordinal() que da la posición de cada constante empezando en cero:
name() da el identificador de la constante como un String, y values() es perfecto para construir menús o iterar sobre opciones. Una advertencia: no guardes el ordinal() en ningún sitio persistente. Si alguien reordena las constantes más adelante, los números cambian y tus datos guardados se rompen. Trata ordinal() como un detalle de implementación, no como un identificador estable.
Los enums son clases de verdad: campos y métodos
Aquí es donde los enums de Java van más allá de los enums de enteros con nombre de otros lenguajes. Cada constante puede llevar datos, pasados a través de un constructor, y el enum puede tener métodos. Primero enumeras las constantes, le das a cada una sus argumentos de constructor, y luego declaras los campos, el constructor y los métodos después de un punto y coma.
Fíjate en la estructura: las constantes van primero y terminan con un punto y coma, y luego sigue el resto de la clase. El constructor es implícitamente private: nunca puedes escribir new Planet(...), porque las únicas instancias permitidas son las declaradas arriba. Los campos final hacen que cada constante sea inmutable, que es exactamente lo que quieres para singletons compartidos.
De una cadena y vuelta
Para convertir una cadena en la constante correspondiente, usa el valueOf autogenerado:
valueOf busca el nombre de la constante de forma exacta, incluyendo mayúsculas y minúsculas, y lanza IllegalArgumentException si no hay coincidencia. Ese es el error habitual: "shipped" no coincide con SHIPPED. Cuando proceses entrada de usuario o datos externos, normaliza primero las mayúsculas (input.toUpperCase()) y envuelve la llamada en un try/catch, o reventará con el primer valor incorrecto.
Comportamiento por constante
A veces cada constante necesita comportarse de forma distinta, no solo guardar datos distintos. Puedes darle a un enum un método abstracto y hacer que cada constante aporte su propia implementación en un pequeño cuerpo:
A esto se le llama a veces el patrón de "método específico de constante". Sustituye un switch enorme sobre la constante por comportamiento adjunto directamente a cada una: añade una nueva operación y el compilador te obliga a definir su apply, así que no puedes olvidarte de ningún caso.
Cuándo recurrir a un enum
Usa un enum siempre que un valor sea, de forma natural, uno de un conjunto pequeño, fijo y conocido en tiempo de compilación:
- Estados de un flujo de trabajo (
PENDING,SHIPPED,DELIVERED). - Categorías, modos o tipos (
READ,WRITE,EXECUTE). - Cualquier lugar donde te tentara definir un grupo de constantes
public static final int: el enum te da los mismos valores con nombre más seguridad de tipos, untoStringlegible y soporte deswitchgratis.
No uses un enum para un conjunto abierto o determinado en tiempo de ejecución (nombres de usuario, una lista de países obtenida de una base de datos). Los enums se fijan en tiempo de compilación; si el conjunto cambia mientras el programa se ejecuta, lo que quieres es una colección normal.
Siguiente: Genéricos
Ya has visto values() devolviendo un Planet[] y, en páginas anteriores, el uso de List<Shape>: esa sintaxis <...> son los genéricos, la forma que tiene Java de escribir una clase o método que funciona para muchos tipos manteniendo plena seguridad de tipos. A continuación analizaremos cómo funcionan realmente List<String> y Map<K, V> y cómo escribir tus propios tipos genéricos.
Preguntas frecuentes
¿Qué es un enum en Java?
Un enum es un tipo especial cuyo valor solo puede ser una de un conjunto fijo de constantes con nombre, como Day.MONDAY o Status.ACTIVE. Cada constante es una única instancia del enum, ya creada. Los enums te dan seguridad de tipos: un método que recibe un Day solo puede recibir un día real, nunca una cadena mal escrita ni un int fuera de rango.
¿Puede un enum de Java tener campos y métodos?
Sí. Un enum es una clase completa. Puedes pasar argumentos al constructor de cada constante, guardarlos en campos private final y añadir métodos que los usen. Por ejemplo, MERCURY(3.3e23, 2.4e6) pasa la masa y el radio al constructor del enum, y un método surfaceGravity() puede calcular el resultado a partir de esos campos.
¿Cuál es la diferencia entre values() y valueOf() en un enum de Java?
values() devuelve un array con todas las constantes del enum, en el orden de declaración, muy útil para recorrer cada opción. valueOf("NAME") hace lo contrario: busca la constante cuyo nombre coincide exactamente con la cadena y lanza IllegalArgumentException si no hay coincidencia. El compilador genera ambos de forma automática.