Ir para o conteúdo

Enumerações

O problema

Antes do Java 5, se fosse necessário representar um padrão de dados enumerado, ou usar uma estrutura como as enumerações em C, o programador de Java teria de recorrer a algo similar a:

public static final int SEGUNDA = 0;
public static final int TERCA = 1;
public static final int QUARTA = 2;
public static final int QUINTA = 3;
public static final int SEXTA = 3;
public static final int SABADO = 3;
public static final int DOMINGO = 3;

O problema do código acima é que não é: * typesafe, já que os dias da semana estão representados como int é possível passar qualquer outro valor no local onde se devia usar as constantes e o programa acabaria num estado incoerente1. * Sem namespace, com o exemplo acima, facilmente o nome de uma variável colidiria com o mesmo nome noutra classe2 ou na mesma classe. Para evitar estas colisões é comum colocar prefixos antes no nome, por exemplo DIA_SEMANA_SEGUNDA. * Código frágil, as constantes são definidas como valores de compilação3, e são compilados no código de clientes as usem4. Caso seja necessário acrescentar valores, ou até mudar a ordem, os clientes precisam ser alterados e recompilados. * Os valores impressos não oferecem qualquer informação sobre as constantes, dado que são simples inteiros, o que é impresso é o seu valor e não o seu nome ou descrição. O programador perde toda a informação associada.

Neste exemplo usamos o tipo int, poderíamos ter usado qualquer outro tipo que os problemas seriam os mesmos.

Alteração à linguagem

Com a versão 5, a linguagem Java foi alterada para contemplar a existência de um novo tipo de dados, o tipo enum. Este tipo de dados é similar o tipo de C, mas oferece novas funcionalidades e mecanismos que não existem tanto em C como noutras linguagens com um tipo de dados enumerados.

O tipo de dados enum é um tipo de dados cujos campos são um conjunto fixo de constantes, tal como o exemplo da secção anterior, mas sem se definir explicitamente o tipo (int, String, double, etc) ou os modificadores static e final

Como são constantes, os nomes dos campos seguem as regras de código Java e são escritos em maiúsculas, pegando no exemplo indicado antes:

public enum DiaSemana {
    SEGUNDA, TERCA, QUARTA, QUINTA, SEXTA, 
    SABADO, DOMINGO
}

O tipo enum define uma classe, que como qualquer outra classe pode ter métodos e atributos mas possui algumas limitações, por exemplo, todas as enumerações estendem automaticamente de java.lang.Enum. Por esta razão não podem estender de outra classe, mesmo que seja outra enumeração já que me Java não há herança múltipla.

O compilador, como noutras classes5, adiciona automaticamente métodos a uma enumeração, por exemplo, todas as enumerações possuem um método estático values que permite obter os valores de uma enumeração como um array e usar a enumeração em ciclos for-each.

for (DiaSemana d : DiaSemana.values()) {
    //...
}

Outra limitação, ou característica, é que o construtor de uma enumeração tem de ter visibilidade privada ou de package, e não pode ser invocado pelo programador. O construtor é responsável por iniciar automaticamente as constantes e é usado apenas pela plataforma sem intervenção do programador.

As enumerações devem ser usadas sempre que seja necessário representar um conjunto fixo de valores, especialmente valores com ordem natural, como os dias da semana, opções de menu, opções de linha de comandos, etc. onde todos os valores são conhecidos durante a compilação. Embora sejam muito parecidas com classes, não devem ser usadas como as classes tradicionais.

Exemplo

O seguinte exemplo foi retirado da documentação oficial do Java e usa uma enumeração para representar os planetas do sistema solar, definidos com propriedades de massa e raio. O tipo enumerado apresenta, em conjunto com o construtor e os atributos, métodos que permitem trabalhar alguns valores de cada planeta

public enum Planeta {
    MERCURIO (3.303e+23, 2.4397e6),
    VENUS   (4.869e+24, 6.0518e6),
    TERRA   (5.976e+24, 6.37814e6),
    MARTE    (6.421e+23, 3.3972e6),
    JUPITER (1.9e+27,   7.1492e7),
    SATURNO  (5.688e+26, 6.0268e7),
    URANO  (8.686e+25, 2.5559e7),
    NEPTUNO (1.024e+26, 2.4746e7);

    private final double massa;   // em quilogramas
    private final double raio; // em metros

    Planeta(double massa, double raio) {
        this.massa = massa;
        this.raio = raio;
    }

    private double massa() {
        return massa;
    }

    private double raio() {
        return raio; 
    }

    // constante gravitacional universal  (m3 kg-1 s-2)
    public static final double G = 6.67300E-11;

    double gravidadeSuperficie() {
        return G * massa / (raio * raio);
    }

    double pesoSuperficie(double otherMass) {
        return otherMass * gravidadeSuperficie();
    }

    public static void main(String[] args) {
        if (args.length != 1) {
            System.err.println("Utilizacao:  java Planeta <peso_na_terra>");
            System.exit(-1);
        }

        double pesoTerra = Double.parseDouble(args[0]);
        double massa = pesoTerra/TERRA.gravidadeSuperficie();
        for (Planeta p : Planeta.values())
           System.out.printf("O teu peso em %s é %f%n", p, p.pesoSuperficie(massa));
    }
}

Se a classe Planeta for executada na linha de comandos, o texto apresentado será:

#java Planet 175
O teu peso em MERCURIO is 66.107583
O teu peso em VENUS is 158.374842
O teu peso em TERRA is 175.000000
O teu peso em MARTE is 66.279007
O teu peso em JUPITER is 442.847567
O teu peso em SATURNO is 186.552719
O teu peso em URANO is 158.397260
O teu peso em NEPTUNO is 199.207413

  1. Claro que seria sempre possível confirmar o valor nos métodos que esperam as constantes mas obriga a que esse controlo seja feito manualmente pelo programador. 

  2. Como é que o programador saberia identificar a constante correcta se duas classes possuíssem a constante SEGUNDA, seria um dia da semana ou uma ordem? 

  3. compile-time 

  4. Por clientes consideramos outras classes ou bibliotecas 

  5. Relembrar que o compilar adicionar sempre um construtor sem argumentos se não for definido nenhum construtor para uma classe