What are the options for initializing a final variable in a Java class?

2

Given the class below, what are the initialization options of the variable ANGLE , since final can be initialized only once?

class Triangle {
    public int base;
    public int height;
    private final double ANGLE;
}
    
asked by anonymous 24.02.2017 / 05:01

1 answer

7

The best way is to use a constructor for this:

class Triangle {
    public int base;
    public int height;
    private final double angle;

    public Triangle(double angle) {
        this.angle = angle;
    }
}

Other less interesting ways would be:

  • Initialize with direct assignment:

    private final double angle = 60.0;
    

    But this form is not very interesting, because in this case you could put the static modifier in it, since all instances will receive the same value. However, you can still have each instance receive a different value:

    private final double angle = Math.random() * 180.0;
    

    A more complex example:

    private static int instanciasCriadas = 0;
    private final numeroDestaInstancia = ++instanciasCriadas;
    
  • Using an instance initializer:

    private final double angle;
    
    {
        angle = Math.random() * 180.0;
    }
    

However, using the builder turns out to be the cleanest and most versatile way to do this. In particular, using instance initializers is something that occurs very rarely in practice. The only two advantages that the instance initialization block can have over the other forms I envision are:

  • A very specific case that consists of a combination of factors where: (a) there are a lot of different constructors; (b) the assignment logic of at least one of the fields is the same independent of the constructor used and does not depend on any of the parameters of the constructor or anything that runs within it, and (c) the steps to initialize are not things that can be represented with a simple expression.

  • Initialization of anonymous class fields that have superclasses, when it needs to use some methods at startup.

  • Both cases are very special, representing code smells and it's always possible to refactor things to avoid them.

    Note that I put angle with lowercase letters, not upper case letters. The reason is that to be considered a constant and then to be written in capital letters, the convention says that the variable must be static final , not just final (there are other rules in addition, but it already starts there).

    Finally, the compiler will move underneath the wipes, all direct assignments to fields not static , as well as the instance initialization blocks, into the constructor. After that, it will parse the resulting constructor code to make sure that all final fields are given a value until the builder finishes execution and only receive it once, giving a compile error otherwise ( more than one assignment or possible non-assignment). This compiler verification almost always ensures that these fields can only be used when properly initialized.

    Why almost always ? Why is there a way to drill this by invoking methods that manipulate these fields from within the constructor or from the instance initialization block before they have been initialized:

    class Ruim {
    
        private final int sempreTres;
    
        public Ruim() {
            mostra();
            sempreTres = 3;
        }
    
        public void mostra() {
            System.out.println(sempreTres);
        }
    
        public static void main(String[] args) {
            Ruim r = new Ruim();
            r.mostra();
        }
    }
    

    Here is the output that this generates:

    0
    3
    

    See here at ideone.

        
    24.02.2017 / 05:46