What is the purpose of the builder of an Enum?

Asked

Viewed 2,210 times

12

In Java constructors are required to create an object in memory, i.e., to instantiate the class that has been defined. Meanwhile, I discovered that a Enum has a builder, but he is not a class, and I thought only classes had builders.

Look at this Enum as an example:

public enum Genero
{
    MASCULINO(1), FEMININO(2);
    public int codigo;

    private Genero(int codigo)
    {
        this.codigo = codigo;
    }        
}

Note:

Consider that the Enum is within a class representing a person, class Pessoa.

See that to use the Enum class does not need to be instantiated, example:

Pessoa.Genero.FEMININO

I would like to know what is the purpose of the builder of a Enum and whether the Enum has the same properties as a class?

  • 1

    Curiosity: although conceptually Enum is not really a class (the constructor cannot be public and it is not you who creates instances of it, for example), it has several characteristics of a regular class, and can have attributes, behaviors and even static members. In fact, looking at the generated bytecode (the compiled java code, so to speak), we can notice that Enums generate the same code as a class after compiled.

  • @Caffe Yes, it is the same thing as a class, but it changes the right concept. You could publish an answer explaining?

  • 1

    That, the concept is different: while a class can have all sorts of functions, the function of Enum should be strictly that of representing a fixed list of constant values (of course list and values can change between compilations - but at runtime it is fixed). Given the flexibility of Enum in Java, I’ve seen programmers implement even complex business rules within Enum - this can bring some difficulties as the system grows. My reply is dispensed with given the excellent answer already posted by @utluiz ;-)

3 answers

11

Java enum constructors are used in situations where you want to add more information to Enum values other than your name. In its example, its Enum has associated with the MALE and FEMALE values codes 1 and 2 (*), respectively. The advantage of this is that you can continue using the names that make sense (MALE, FEMALE), but when storing the values (for example, in a database) you would store the value associated with them (again, in this case, assuming your comic book scheme uses 1 for men and 2 for women).

Other advantages of associating more data to Enum values is the possibility for you to add a logic to Enum. An example I’ve seen is of an SDK, where each Enum value has a code (string), and an extra function on Enum is used to concatenate all values from one EnumSet.

(*) - Declare the field that stores the aggregate value as public is not appropriate as poorly written code can change it and your program may start to have hard to identify errors.

  • The code field should be private in this case?

  • 1

    Yes, private, with a get method to access it, something like public int getCodigo() { return this.codigo; }

  • 3

    @Denercarvalho Another option is to declare as final, thus: public final int codigo;, so it also cannot be modified after having its value assigned in the Enum constructor. I use a lot of final in these situations and other data structures to avoid "get".

7


TL;DR

Enums constructors have exactly the same purpose as constructors in classes, initialize values. Enums are nothing more than a specific type of class.

Enums are classes

The first thing to definitely understand Enums is that they are nothing more than classes with some specific characteristics and "syntactic sugar".

Let’s see, the question has the following example of Enum:

public enum Genero
{
    MASCULINO(1), FEMININO(2);
    public int codigo;

    private Genero(int codigo)
    {
        this.codigo = codigo;
    }
}

We could easily rewrite the above example as a normal class:

public class EnumGenero {
    public static final EnumGenero MASCULINO = new EnumGenero(1), FEMININO = new EnumGenero(2);
    public int codigo;

    private EnumGenero(int codigo)
    {
        this.codigo = codigo;
    }
}

And the use is practically equal:

EnumGenero masculino1 = EnumGenero.MASCULINO;
Genero masculino2 = Genero.MASCULINO;

Important points:

  1. Enums constants are nothing more than static attributes. See in the example how I introduce the class into constants. It’s the same thing with Enum, the difference is that you don’t need to put the access modifiers or the new, but the behavior is exactly the same.
  2. Enums are instantiated exactly like classes. As I said in the previous item, the difference is that the new is implicit in the simplified Enum syntax.
  3. The builder of Enum is always private And you don’t have to put this modifier in, it’s redundant. In the case of the class, it depends on you placing it there to prevent other instances from being created.

Enums vs. Classes

Is there any difference between using Enums and classes like the one I put above? Let’s consider the differences for the above example.

About the Enum:

  • Can use with switch and some Ides warn in case you forget to put some value.
  • Simplified syntax that gives certain guarantees, such as that there will be no instances other than predefined constants.
  • Auxiliary methods such as name(), ordinal(), valueOf() and values().

About the class:

  • More flexibility, allows inheritance and other constructions, although this is usually not recommended unless there is a very strong reason.
  • Disadvantage of you having to manually add all access modifiers in addition to the instantiation of each constant.
  • It does not have Enum auxiliary methods by default, but can be implemented, although not in a very simple and safe way.

Comments on the implementation of the Enum

Encapsulation

Your Enum may have the attribute codigo accessed via getter:

public enum Genero {
    MASCULINO(1), FEMININO(2);
    private int codigo;

    private Genero(int codigo) {
        this.codigo = codigo;
    }      
    public int getCodigo() {
        return codigo;
    }  
}

Is there any need for this? Technically not.

Although it is generally desirable to encapsulate the attributes so as not to allow direct access via external code, this argument is not as strong when we speak of constants.

The value does not change and there is no strong reason in this specific case to encapsulate an attribute that has very low probability of changing in the future.

However, the strongest reasons to use a getter is to maintain consistency (it is bad to work in a system where now attributes are exposed, sometimes they are not) and to enable internal refactoring in the future, even if this is not visible now.

A situation where I nay recommends using encapsulation is in internal classes where the use of the value is private. Example:

public class ProcessaAlgoComGenero {
    public enum Genero {
        MASCULINO(1), FEMININO(2);
        private int codigo;
        Genero(int codigo) {
            this.codigo = codigo;
        }
    }
    public void processaGenero(Genero g) {
        System.out.println(g.codigo);
    }
}

And the call goes like this:

new ProcessaAlgoComGenero().processaGenero(ProcessaAlgoComGenero.Genero.MASCULINO);

Note that the attribute codigo is only accessible within the class ProcessaAlgoComGenero. Encapsulating an attribute used only internally is unnecessary.

Retrieving values by code

It is common when we add attributes to Enum values we need to recover constants based on that value.

While to recover the constant based on the name using the ready method valueOf(), we have to build the other method ourselves.

So if at any time you need to retrieve the Enum by codigo, can do this:

public static Genero ofCode(int codigo) {
    for (Genero g : values()) {
        if (g.codigo == codigo) {
            return g;
        }
    }
    return null; //ou lança uma exceção
}

Note that the method ofCode traverse all Enum values through the array returned by values() and search for the constant with the codigo to be recovered.

The advantage of this code instead of a if or switch is that you can reuse it for any situation, including other Enums, with a minimum of change.

You could just do it like this:

public static Genero ofCode(int codigo) {
    if (MASCULINO.codigo == codigo) return MASCULINO;
    if (FEMININO.codigo == codigo) return FEMININO;
    return null; //ou lança uma exceção
}

But in this case for each One you would have to rewrite everything.

If there are too many constants and the running time is a concern, you can use a map to make the search time constant.

First you would need to initialize the map as soon as Enum is initialized. Just add this in Enum:

static Map<Integer, Genero> cache = new HashMap<>();
static {
    for (Genero g : values()) {
        cache.put(g.codigo, g);
    }
}

And then the method ofCode is trivial and efficient:

public static Genero ofCode(int codigo) {
    return cache.get(codigo);
}

Considerations

Using Enum is good, facilitates coding and makes code safer against misconceptions and even against "smart-ass programmers".

Enums are flexible as classes, although with several other restrictions. As seen in the example above, it is possible to create complex constructions within Enums. However, use this only in cases where it is really necessary, otherwise you are adding complexity and unnecessary headaches to your program.

6

Works similarly, but you can’t call the builder yourself.

In the example you put, the constructor is called twice and MASCULINO and FEMININO are initialized with past value (1 and 2 respectively).

The main difference of this Enum to a class is that on Enum, all objects declared at the beginning are started automatically and no more after that.

  • In this box to use the Enum I must do it: Pessoa.Genero.FEMININO the class is not instantiated to use it.

  • Actually, I think the example is bad. In this case it would be better not to use the constructor, to be able to access as you commented. .

  • Yes, but I still can not understand the purpose of the builder since it is not possible to do Pessoa.Genero genero = new Pessoa.Genero(2), the builder would really be needed at Enum?

  • It would be useful if Enum were something more complex. If instead of gender, it was an Enum of personas. You could have defined in Enum some attributes like Gender, Age, Profession, etc.

  • How so more complex?

  • Mal, I ended up editing while you were asking.

Show 1 more comment

Browser other questions tagged

You are not signed in. Login or sign up in order to post.