Java Enum. Enumerados en Java

Un enum, también llamado enumeración o tipo enumerado es un tipo de dato definido por el usuario que solo puede tomar como valores los definidos en una lista de constantes.
Un enum se declara de forma general:
[modificadores] enum nombreEnum {VALOR1, VALOR2, VALOR3, ...}          
modificadores (opcional) puede ser public, private, protected además de static.
enum es la palabra reservada para definir enumeraciones en Java.
nombreEnum es el nombre del nuevo tipo creado.
VALOR1, VALOR2, ... son los valores que pueden tomar las variables que se declaren de este tipo.
Se pueden declarar enumeraciones dentro o fuera de una clase pero nunca dentro de un método.
Podemos considerar utilizar un tipo enumerado cuando una variable del programa solamente pueda tomar unos valores determinados.
Ejemplos:
enum DiaSemana {LUNES, MARTES, MIÉRCOLES, JUEVES, VIERNES, SABADO, DOMINGO};          
enum Nivel {ALTO, MEDIO, BAJO};                                                       
enum Direccion {NORTE, SUR, ESTE, OESTE};                                             
Por convenio se suelen escribir los valores del enum en mayúsculas ya que se trata de constantes.
Una vez creado el tipo enum ya podemos declarar variables.
Para declarar una variable de tipo DiaSemana escribimos:
DiaSemana d;          
La variable d solo podrá tomar uno de los valores definidos en la lista de valores.
Para darle un valor a las variables lo haermos escribiendo nombreDelEnum.valor;
Por ejemplo:
d = DiaSemana.JUEVES;          

Ejemplo básico de uso de un tipo enumerado:

Creamos un enum llamado DiaSemana, si estamos haciendo un proyecto que contenga varias clases, lo normal es que el enum se escriba un archivo aparte, de forma similar a como se hace con las clases.




Creamos una clase principal para utilizarlo:
public class EjemploEnum {

    public static void main(String[] args) {

        DiaSemana d; // declaramos una variable del tipo DiaSemana                                                

        d = DiaSemana.DOMINGO; //asignamos a d un valor
 
        //Ejemplo de if para comparar una variable de tipo enum con un valor
        if (d == DiaSemana.DOMINGO || d == DiaSemana.SABADO)  
            System.out.println("Estamos en fin de semana");
        else 
            System.out.println("Aún no ha llegado el fin de semana");

        //Ejemplo de uso de una variable de tipo enum en un switch                                                
        switch (d) {  
            case LUNES:
            case MARTES:
            case MIERCOLES:
            case JUEVES:
            case VIERNES:
                System.out.println("Aún no ha llegado el fin de semana");                                         
                break;
            default:
                System.out.println("Estamos en fin de semana");
        }
    }
}

Los tipos enumerados en Java son mucho más potentes que sus equivalentes en lenguajes como C++:

  • Un enum en Java es una Clase
  • Cada constante del enum es un objeto. NO se pueden crear nuevos objetos del enum. Todos los objetos de la clase son los que aparecen en la declaración del enum.
  • Por ser una clase, un enum puede contener atributos y métodos.
  • Todas las clases enum creadas heredan de forma implícita de la clase Enum de Java.
Algunos métodos que se heredan de la clase Enum de Java:

name()

public final String name()
Devuelve un String con el nombre de la constante que contiene tal y como aparece en la declaración del enum.

ordinal()

public final int ordinal()
Devuelve un entero con la posición de la constante según está declarada en el enum. A la primera constante le corresponde la posición cero.

toString()

public String toString()
Devuelve un String con el nombre de la constante que contiene tal y como aparece en la declaración del enum.
Sobrescribe el método toString de la clase Object.

equals()

public final boolean equals(Object other)
Devuelve true si los valores de dos enum son iguales.
Sobrescribe el método equals de la clase Object.
También se puede comprobar si dos enum son iguales utilizando el operador ==

compareTo()

public final int compareTo(Enum other)
Compara el enum con el que recibe según el orden en el que están declaradas las constantes. Devuelve un número negativo, cero o un número positivo según el objeto sea menor, igual o mayor que el que recibe como parámetro.
Solo se pueden comparar enumeraciones del mismo tipo.

valueOf()

public static enumConstant valueOf(String s)
Devuelve la constante que coincide exactamente con el String que recibe como parámetro.
values()
public static enumConstant [] values()
Devuelve un array que contiene todas las constantes de la enumeración en el orden en que se han declarado. Se suele usar en bucles for each para recorrer el enum.

https://docs.oracle.com/javase/10/docs/api/java/lang/Enum.html 
Ejemplo de uso de estos métodos, utilizando el enum DiaSemana creado anteriormente:

public static void main(String[] args) {
       DiaSemana dia; //declaración de una variable tipo enum

       dia = DiaSemana.MIERCOLES; //Se le asigna un valor

       // dia.name() devuelve un String con el valor de la constante (MIERCOLES)
       System.out.println(dia.name());

       // día.toString() devuelve un String con el valor de la constante (MIERCOLES)                              
       System.out.println(dia.toString());

       // Devuelve un entero con la posición que ocupa dentro del enum (2).
       System.out.println(dia.ordinal());   

       // Comprobar la igualdad mediante equals y ==
       if(dia.equals(DiaSemana.LUNES)){ //Esta condición no se cumple
           System.out.println("El valor es LUNES");
       }
       if(dia.equals(DiaSemana.MIERCOLES)){ //Esta condición se cumple
           System.out.println("El valor es MIERCOLES usando equals");
       }
       if(dia == DiaSemana.MIERCOLES){ //Esta condición se cumple
           System.out.println("El valor es MIERCOLES usando ==");
       }

       //Se crea otra variable y se le asigna un valor 
       DiaSemana otroDia = DiaSemana.MARTES;

       // Comparamos las dos variables enum mediante compareTo
       // Se comparan según el orden en el que se han declarado en el enum                                        
       // En este caso el if se cumple
       // dia contiene MIERCOLES y otroDia contiene MARTES
       // En la declaración del enum MIERCOLES aparece después de MARTES 
       // por lo tanto es mayor
       if (dia.compareTo(otroDia) > 0) { //Esta condición se cumple: dia > otroDia 
            System.out.println(dia + " > " + otroDia);
       } else {
            System.out.println(dia + " <= " + otroDia);
       }
       //Mostrar todos los elementos del enum
       //el método values() devuelve un array con todas las constantes del enum                                   
       for (DiaSemana d : DiaSemana.values()) {
            System.out.print(d + " ");
       }
}

Asignar valores a un enum desde teclado

Para asignar valores a una variabale enum desde teclado podemos hacerlo de varias formas. Algunas de ellas aparecen en los siguientes ejemplos. Seguimos utilizando el enum DiaSemana.

Ejemplo 1: Leer un valor entero por teclado y hacer un switch. Según el valor introducido se asigna al enum la constante correspondiente.
Scanner sc = new Scanner(System.in);
int valor;
DiaSemana d = null;  //Asigno null para evitar el warning de NetBeans: la variable d                              
                     //podría no estar inicializada 
do {
    System.out.print("Introduce día de la semana del 1(Lunes) al 7(Domingo): ");
    valor = sc.nextInt();
} while (valor < 1 || valor > 7);

switch(valor){
       case 1: d = DiaSemana.LUNES;
               break;
       case 2: d = DiaSemana.MARTES;
               break;
       case 3: d = DiaSemana.MIERCOLES;                                                                           
               break;
       case 4: d = DiaSemana.JUEVES;
               break;
       case 5: d = DiaSemana.VIERNES;
               break;
       case 6: d = DiaSemana.SABADO;
               break;
       case 7: d = DiaSemana.DOMINGO;
               break;
}

Ejemplo 2: Igual que en el ejemplo anterior pero utilizando if anidados.
Scanner sc = new Scanner(System.in);
int valor;
DiaSemana d;
do {
    System.out.print("Introduce día de la semana del 1(Lunes) al 7(Domingo): ");                                  
    valor = sc.nextInt();
} while (valor < 1 || valor > 7);
if(valor == 1){
   d = DiaSemana.LUNES;
}else if(valor == 2){
         d = DiaSemana.MARTES;
}else if(valor == 3){
         d = DiaSemana.MIERCOLES;
}else if(valor == 4){
         d = DiaSemana.JUEVES;
}else if(valor == 5){
         d = DiaSemana.VIERNES;                                                                                   
}else if(valor == 6){
         d = DiaSemana.SABADO;
}else{
      d = DiaSemana.DOMINGO;
}

Ejemplo 3: Leer un String que coincida exactamente con el valor de uan constante del enum y utilizar el método valueOf para asignar ese valor a la variable enum.
Scanner sc = new Scanner(System.in);
DiaSemana d;
String dia;
System.out.print("Introduce día de la semana (LUNES, MARTES, ...): ");                                            
dia = sc.nextLine();
d = DiaSemana.valueOf(dia.toUpperCase());                                                                         

Si se hace de esta forma debemos asegurarnos que se ha introducido por teclado un valor que coincide con una de las constantes declaradas en el enum. Si el String introducido por teclado no coincide con ninguna constante del enum, el método valueOf  provocará un error de ejecución.


Ejemplo 4: Utilizando el método values. Este método devuelve un array con todas las constantes del enum. En el caso del eum DiaSemana devolverá el siguiente array de tipo DiaSemana:


LUNES
MARTES
MIERCOLES
JUEVES
VIERNES
SABADO
DOMINGO
0
1
2
3
4
5
6

Se introduce un valor entero por teclado y se utiliza como índice del array para obtener la constante.
Scanner sc = new Scanner(System.in);
DiaSemana d;                                                                                                      
int valor;
System.out.print("Introduce día de la semana del 1(Lunes) al 7(Domingo) ");                                       
valor = sc.nextInt();
d = DiaSemana.values()[valor - 1];          

Esta es la mejor opción cuando el tipo enumerado tiene muchas constantes. Nos evitamos hacer un switch o una cadena de if anidados demasiado grandes.

Uso avanzado de tipos enumerados


En Java podemos declarar constantes de un enum relacionadas con un valor.
Para ello la clase enum debe tener un atributo para cada valor relacionado con la constante y un constructor para asignar los valores.
Esos valores se pasan al constructor cuando se crea la constante.
El constructor debe tener acceso private o package. No puede ser público ya que no se pueden crear más objetos del enum.
Las constantes se deben definir primero, antes que cualquier otro atributo o método.
Cuando un enum tiene atributos o métodos la lista de constantes debe acabar con punto y coma.
Los valores relacionados con las constantes se obtienen mediante el método get correspondiente.
Este tipo de enum no debe contener métodos set ya que los valores relacionados con las constantes se asignan en el constructor cuando se crean y ya no se pueden modificar. 

Ejemplo de uso avanzado de un tipo enumerado
El enum Precios contiene dos constantes con valores asociados. Los valores 100 y 80 que acompañan a las constantes son las que se pasan al constructor.

//Declaración del enum
public enum Precios { 
     PRECIONORMAL(100), PRECIOVIP(80);  //los valores se pasan al constructor                                     
     private double precio;
     private Precios(double p){
         precio = p;
     }
     public double getPrecio() {
         return precio;
     }
}
//Clase principal
public class Enum3 {  
    public static void main(String[] args) {
        Precios p = Precios.PRECIOVIP;     //precio = 80                                                          
        System.out.println(p.getPrecio()); //muestra 80
    }
}

Llenar un array con números aleatorios

En esta entrada vamos a escribir un método Java que llene un array de enteros con números aleatorios.
Los números aleatorios deberán estar comprendidos entre dos límites (desde,  hasta) ambos incluidos.
El método recibe como parámetros los valores desde, hasta y el tamaño del array.
El método devuelve mediante return el array de enteros creado.
Para obtener números enteros aleatorios usaremos el método nextInt() de la clase Random.
Para que los números aleatorios obtenidos estén dentro de los limites <desde, hasta> utilizaremos el método nextInt() de la siguiente forma:
nextInt(hasta - desde + 1) + desde

public static int [] llenarArrayAleatorio(int desde, int hasta, int tam){
        int[] numeros = new int[tam];                                                                             
        Random rnd = new Random();
        for (int i = 0; i < numeros.length; i++) {
             numeros[i] = rnd.nextInt(hasta - desde + 1) + desde;                                                 
        }
        return numeros;
}

Si los números no se pueden repetir debemos complicar un poco más el código.
En este caso cada vez que se obtiene un número aleatorio se debe comprobar si ya está en el array.
Para hacer este trabajo vamos a escribir un método llamado comprobarSiContiene. A este método se le envía el array, la posición del elemento actual y el número aleatorio a insertar y devuelve true si el número ya existe en el array. En ese caso se vuelve a sacar otro número aleatorio.

public static int[] llenarArrayAleatorio(int desde, int hasta, int tam) {
        int[] numeros = new int[tam];
        Random rnd = new Random();
        int n;
        for (int i = 0; i < numeros.length; i++) {                                                                
            do {
                n = rnd.nextInt(hasta - desde + 1) + desde;
            } while (comprobarSiContiene(numeros, i, n));
            numeros[i] = n;
        }
        return numeros;
}

public static boolean comprobarSiContiene(int[] numeros, int indice, int valor) {                                 
        for (int i = 0; i < indice; i++) {
            if (numeros[i] == valor) {
                return true;                                                                                      
            }
        }
        return false;
}

Esta sería la solución para aquellos que están empezando a programar. Pero en este caso nos podemos ahorrar trabajo utilizando la API de Java. En concreto utilizando un ArrayList y el método shuffle de Collections.

La idea es llenar un ArrayList con todos los números comprendidos entre los límites desde/hasta. A continuación se llama al método shuffle para desordenarlos y después se pasan al array de enteros los tam primeros elementos, donde tam es el tamaño del array a devolver.

El método utilizando el ArrayList y Collections.shuffle quedaría así:

private static int[] llenarArrayAleatorio(int desde, int hasta, int tam) {
        ArrayList<Integer> array = new ArrayList<>();                                                         
        for (int i = desde; i <= hasta; i++) {
            array.add(i);
        }
        Collections.shuffle(array);
        int [] numeros = new int[tam];
        for(int i = 0; i < numeros.length; i++){                                                                  
            numeros[i]=array.get(i);
        }
        return numeros;                                                                                           
}

Clase StringTokenizer

La clase StringTokenizer sirve para separar una cadena de caracteres en una serie de elementos o tokens.
Se incluye en el paquete java.util
Los tokens se separan mediante delimitadores.
Los delimitadores por defecto son:
espacio en blanco
tabulador \t
salto de línea \n
retorno \r
avance de página \f
Un objeto StringTokenizer se construye a partir de un objeto String.
Para obetener los tokens del String podemos utilizar los métodos hasMoreTokens() y nextToken().
hasMoreTokens() devuelve true si hay más tokens que obtener en la cadena.
nextToken() devuelve un String con el siguiente token. Lanza una excepción del tipo NoSuchElementException si no hay más tokens.
Otro método importante es countTokens() que devuelve la cantidad de tokens que aun quedan por extraer
Ejemplo de uso de StringTokenizer:
String s = "blanco, rojo, verde y azul";
StringTokenizer st = new StringTokenizer(s);                                                                      
while (st.hasMoreTokens())                                                                                        
       System.out.println(st.nextToken());
La salida que se obtiene por pantalla es:
blanco,
rojo,
verde     
y
azul     
Se ha separado el String s en tokens separados por el delimitador por defecto. En este caso el espacio en blanco.
Si lo representamos de forma gráfica, el funcionamiento es el siguiente:
La instrucción
StringTokenizer st = new StringTokenizer(s);     
Produce lo siguiente:















Se separa el String en tokens y el objeto st apunta al primero.
A continuación
st.hasMoreTokens()     
comprueba si hay tokens que extraer. En este caso st apunta a un token por lo tanto hasMoreTokens() devuelve true.
A continuación
st.nextToken()          
Obtiene el token al que apunta st (blanco,) y avanza al siguiente.














El ciclo while repite el proceso.
Cuando se alcanza el último token y se avanza el siguiente la condición del while será false.


Los delimitadores se pueden especificar cuando se crea el objeto StringTokenizer.
Por ejemplo, para indicar que los delimitadores son la coma y el espacio en blanco:
StringTokenizer st = new StringTokenizer("colores rojo, verde y azul", ", ");     
La ejecución del while anterior obtendría la salida:
colores
rojo     
verde    
y
azul
la coma no aparece ya que se ha especificado como delimitador y los delimitadores no aparecen.
Si queremos que aparezcan se debe escribir true como tercer argumento en el constructor:
StringTokenizer st = new StringTokenizer("colores rojo, verde y azul", ", ", true);     
En este caso la salida es:
colores

rojo
,

verde     

y

azul