Enums avanzados con métodos
flowchart TB
Op[enum Operación] --> Plus[PLUS]
Op --> Min[MINUS]
Op --> Mul[TIMES]
Op --> Div[DIVIDE]
Plus --> Apl[apply x y]
Min --> Apl2[apply x y]
Mul --> Apl3[apply x y]
Div --> Apl4[apply x y]
Op --> Sw[Switch expressión sobre enum]
Op --> Map[EnumMap EnumSet]
Op --> Ser[Serialización segura]
Enums con campos y comportamiento
Los enums en Java son clases completas: pueden tener campos, constructores y métodos. Esto los hace mucho más expresivos que simples listas de constantes.
public enum Planeta {
MERCURIO(3.303e+23, 2.4397e6),
VENUS (4.869e+24, 6.0518e6),
TIERRA (5.976e+24, 6.37814e6),
MARTE (6.421e+23, 3.3972e6);
private final double masa; // kg
private final double radio; // m
// Constructor (privado por defecto)
Planeta(double masa, double radio) {
this.masa = masa;
this.radio = radio;
}
public double masa() { return masa; }
public double radio() { return radio; }
public double gravedadSuperficial() {
final double G = 6.67300E-11;
return G * masa / (radio * radio);
}
}
// Uso
double g = Planeta.TIERRA.gravedadSuperficial(); // ~9.81
for (Planeta p : Planeta.values()) {
System.out.println(p + ": g=" + p.gravedadSuperficial());
}
El constructor es implícitamente privado (no se pueden crear enums desde fuera). Las constantes se declaran una vez y viven durante toda la ejecución.
Constant-specific methods (métodos por constante)
Cada constante del enum puede sobrescribir un método abstracto, dándole comportamiento específico:
public enum Operacion {
SUMA {
@Override public double aplicar(double a, double b) { return a + b; }
},
RESTA {
@Override public double aplicar(double a, double b) { return a - b; }
},
MULTIPLICAR {
@Override public double aplicar(double a, double b) { return a * b; }
},
DIVIDIR {
@Override public double aplicar(double a, double b) {
if (b == 0) throw new ArithmeticException("división por cero");
return a / b;
}
};
public abstract double aplicar(double a, double b);
}
// Uso
double r = Operacion.SUMA.aplicar(3, 4); // 7
double r2 = Operacion.DIVIDIR.aplicar(10, 2); // 5
Esta técnica es una forma concisa del patrón Strategy: cada constante es una estrategia diferente para la misma operación conceptual.
EnumSet: conjunto optimizado
EnumSet<E> es una implementación de Set<E> especializada para enums. Internamente usa un bitmap (un long por cada 64 constantes) con rendimiento extraordinario.
public enum Dia { LUN, MAR, MIE, JUE, VIE, SAB, DOM }
EnumSet<Dia> laborales = EnumSet.range(Dia.LUN, Dia.VIE);
EnumSet<Dia> finDeSemana = EnumSet.of(Dia.SAB, Dia.DOM);
EnumSet<Dia> todos = EnumSet.allOf(Dia.class);
EnumSet<Dia> vacio = EnumSet.noneOf(Dia.class);
// Operaciones
EnumSet<Dia> complemento = EnumSet.complementOf(laborales); // {SAB, DOM}
EnumSet<Dia> copia = EnumSet.copyOf(laborales);
// Uso como Set
if (laborales.contains(Dia.LUN)) { ... }
EnumSet es mucho más rápido que HashSet<Dia> porque las operaciones son bit manipulation puras: union es |, intersect es &, complement es ~. Úsalo siempre que necesites un conjunto de constantes enum.
Casos de uso
// Permisos tipados
enum Permiso { LEER, ESCRIBIR, EJECUTAR, ADMIN }
class Usuario {
EnumSet<Permiso> permisos;
boolean puede(Permiso p) {
return permisos.contains(p);
}
boolean puedeTodo() {
return permisos.containsAll(EnumSet.allOf(Permiso.class));
}
}
Es más eficiente y legible que un Set<String> o flags bitwise manuales.
EnumMap: mapa optimizado
EnumMap<K extends Enum<K>, V> usa un array indexado por ordinal como almacenamiento. Tiempo constante O(1) garantizado.
EnumMap<Dia, String> agenda = new EnumMap<>(Dia.class);
agenda.put(Dia.LUN, "reunión sprint");
agenda.put(Dia.MAR, "revisión de código");
agenda.put(Dia.VIE, "retrospectiva");
for (var entry : agenda.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
Las entradas se iteran en el orden de declaración del enum, no de inserción ni alfabético.
Integración con switch
Los enums se benefician especialmente del switch expressión exhaustivo:
int puntuacion(Dia dia) {
return switch (dia) {
case LUN, MAR -> 1;
case MIE -> 2;
case JUE, VIE -> 3;
case SAB, DOM -> 0;
// no necesita default: el compilador sabe que Dia solo tiene estas 7 constantes
};
}
Si añades una constante FERIADO al enum y no actualizas el switch, el compilador falla: refactor guiado.
Métodos estáticos y de instancia del enum
Todos los enums heredan de java.lang.Enum varios métodos útiles:
| Método | Descripción |
|--------|-------------|
| name() | Nombre textual ("LUN") |
| ordinal() | Posición 0-indexada en la declaración |
| values() | Array con todas las constantes (método estático) |
| valueOf(String) | Busca constante por nombre (estático) |
| getDeclaringClass() | Clase del enum |
String nombre = Dia.LUN.name(); // "LUN"
int indice = Dia.LUN.ordinal(); // 0
Dia[] todos = Dia.values(); // todas
Dia lun = Dia.valueOf("LUN"); // parsea desde String
Nunca uses ordinal() para lógica de negocio (es frágil: si reordenas las constantes, todo se rompe). Úsalo solo para propósitos técnicos como indexar arrays.
Enums implementan interfaces
Un enum puede implementar una interfaz, permitiendo polimorfismo:
interface Transformacion {
String aplicar(String s);
}
enum FormatoTexto implements Transformacion {
MAYUSCULAS {
@Override public String aplicar(String s) { return s.toUpperCase(); }
},
MINUSCULAS {
@Override public String aplicar(String s) { return s.toLowerCase(); }
},
INVERSA {
@Override public String aplicar(String s) { return new StringBuilder(s).reverse().toString(); }
};
}
// Uso polimórfico
Transformacion t = FormatoTexto.MAYUSCULAS;
t.aplicar("hola");
Patrón Singleton con enum
La forma más limpia de Singleton en Java (recomendada por Joshua Bloch):
public enum ConexionDB {
INSTANCIA;
private final String url = "jdbc:postgresql://localhost/mydb";
public void ejecutar(String sql) { /* ... */ }
}
// Uso
ConexionDB.INSTANCIA.ejecutar("SELECT 1");
Ventajas sobre el Singleton clásico:
- Thread-safe por construcción de la JVM.
- Reflection-safe: no se puede instanciar desde fuera.
- Serialization-safe: preserva identidad tras deserializar.
State machines con enum + transiciones
enum Estado {
PENDIENTE {
@Override public Estado siguiente() { return EN_PROCESO; }
},
EN_PROCESO {
@Override public Estado siguiente() { return COMPLETADO; }
},
COMPLETADO {
@Override public Estado siguiente() { return this; } // terminal
},
CANCELADO {
@Override public Estado siguiente() { return this; } // terminal
};
public abstract Estado siguiente();
}
Estado actual = Estado.PENDIENTE;
actual = actual.siguiente(); // EN_PROCESO
actual = actual.siguiente(); // COMPLETADO
Buenas prácticas
- Los enums son inmutables por defecto (sus constantes se crean una vez). No añadas setters.
- Si guardas colecciones como campos de enum, usa
List.copyOfoCollections.unmodifiableListpara mantener la inmutabilidad. - Prefiere métodos por constante (constant-specific methods) a un
switchexterno cuando el comportamiento sea parte de la identidad de la constante. EnumSetyEnumMapson tus colecciones por defecto para claves enum. Son más rápidas y consumen menos memoria.- No abuses de
values()en bucles críticos: crea un nuevo array cada vez. Cachea si hace falta.
Los enums son una de las construcciones más infravaloradas de Java. Dominarlos bien eleva la calidad del código con mínimo esfuerzo.
Alan Sastre
Ingeniero de Software y formador, CEO en CertiDevs
Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, Java es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.
Más tutoriales de Java
Explora más contenido relacionado con Java y continúa aprendiendo con nuestros tutoriales gratuitos.
Aprendizajes de esta lección
Añadir campos y métodos a enums. Definir métodos abstractos implementados por cada constante (constant-specific methods). Usar EnumSet y EnumMap con su rendimiento óptimo. Integrar enums con switch exhaustivo. Iterar constantes con values() y valueOf(String). Aplicar enums como Singleton y state machines.