Why and when to use enum in Java?

33

Someone learning the language, novice or experienced, may never have come across Java enumerations. I have read on several occasions that they are useful, for example, to implement singleton standards with more guarantees of visibility between threads.

What is the use of learning and using enum in day-to-day programming?

    
asked by anonymous 25.02.2014 / 23:17

1 answer

36

The enum represents a fixed set of values, in a more or less autodocumented form. They make the code more explicit, more readable, and less vulnerable to programming errors.

A common alternative is to use String or int for constant values. The enum has some advantages over these types:

  • The compiler does not allow typographical errors, as can happen with string literals.

  • The compiler does not allow values that are outside the enumerated set, which is a consequence of the enumerations being types themselves.
  • You do not need to write preconditions , or manual tests, to ensure that the argument of a method falls within the accepted range of values.

    The type invariant is free, again because the enumerations are types, and they define the valid values at the start.

  • Enumerations can define behavior (methods) for their constants, as in any usual class.

  • Constants in an enumeration can specialize their behavior: each constant can have its own definition of a method.

  • The virtual machine gives guarantees of thread safety when loading the enumeration.

  • They can also be used in switch .

There are still some creative uses of enumerations, such as state machines, as seen in this blog .

Let's take a practical example of these advantages. Imagine a program that gets two colors and tries to combine them according to the RGB system.

Using int

public static final int
    VERMELHO    = 1,
    AZUL        = 2,
    VERDE       = 4,
    AMARELO     = 5,
    CIANO       = 6,
    ROXO        = 3,
    BRANCO      = 7;

/**
 * @pre cor1 == VERMELHO || cor1 == AZUL || cor1 == VERDE ||
 *    cor1 == CIANO || cor1 == AMARELO || cor1 == ROXO || cor1 == BRANCO;
 * @pre cor2 == VERMELHO || cor2 == AZUL || cor2 == VERDE ||
 *    cor2 == CIANO || cor2 == AMARELO || cor2 == ROXO || cor2 == BRANCO;
 * @post cor1 == VERMELHO && cor2 == VERDE => return == AMARELO;
 * @post ...
 */
public static int combina(int cor1, int cor2) {
    if (!corValida(cor1)) return -1;
    if (!corValida(cor2)) return -1;
    return cor1 | cor2;
}

private boolean corValida(int cor) {
    return cor == VERMELHO || cor == VERDE || cor == AZUL ||
        cor == AMARELO || cor == CIANO || cor == ROXO || cor == BRANCO;
}

public static void main(String[] args) {
    int cor = combina(1, 2);
    // O que acontece quando VERMELHO e AZUL deixam de ser 1 ou 2?
    assert cor == ROXO;
}

Using enum

/*
 * Os codigos numéricos não fazem qualquer falta.
 * Seria possível resolver com switch, mas ficaria muito extenso para exemplo.
 * Contudo, um caso real deveria retirar os códigos,
 * para maiores garantias de que não há erro humano.
 */
public static enum Cor {
    VERMELHO(1), AZUL(2), VERDE(4), AMARELO(5), CIANO(6), ROXO(3), BRANCO(7);
    private final int codigo;

    Cor(int codigo) { this.codigo = codigo; }

    int codigo() { return codigo; }

    public static Cor porCodigo(int codigo) {
        for (Cor cor: Cor.values()) {
            if (codigo == cor.codigo()) return cor;
        }
        throw new IllegalArgumentException("codigo invalido");
    }
}

/**
 * @pre cor1 != null && cor2 != null;
 * @post cor1 == Cor.VERMELHO && cor2 == Cor.VERDE => return == Cor.AMARELO;
 * @post ...
 */
public static int combina(Cor cor1, Cor cor2) {
    int combinado = cor1.codigo() | cor2.codigo();
    return Cor.porCodigo(combinado);
}

public static void main(String[] args) {
    Cor cor = combina(Cor.VERMELHO, Cor.AZUL);
    assert cor == Cor.ROXO;
}
    
25.02.2014 / 23:17