dido
dido

Reputation: 3407

Do final variables need to be given a value immediately after declaring them

Is it possible to assign a value to a final variable anywhere else in the program? Or is it mandatory that they be assigned a value upon creation?

class TestClass() {
//this won't compile but is it possible to assign str a value anywhere else in the program?
public static final String str;
}

Upvotes: 2

Views: 182

Answers (3)

oksayt
oksayt

Reputation: 4365

Local variables

A final variable needs to be assigned a value exactly once before it is accessed. This means that if it's never assigned a value and never accessed, the compiler won't complain.

void foo() {
    final int a; // Never assigned, but never accessed either, so no problem
    final int b = 7;

    System.out.println("b is " + b);

    // System.out.println("a is " + a);
    // Uncommenting the above line would cause a compile error
}

Static fields

A similar logic applies to final static fields, except it's assumed that they will be accessed at some point, so they must be initialized either on the definition line or in a static initializer block.

Here's what the Java tutorial has to say about static initialization blocks:

This works well when the initialization value is available and the initialization can be put on one line. However, this form of initialization has limitations because of its simplicity. If initialization requires some logic (for example, error handling or a for loop to fill a complex array), simple assignment is inadequate. Instance variables can be initialized in constructors, where error handling or other logic can be used. To provide the same capability for class variables, the Java programming language includes static initialization blocks.

Note: It is not necessary to declare fields at the beginning of the class definition, although this is the most common practice. It is only necessary that they be declared and initialized before they are used.

Instance fields

While we're at it, a final instance (non-static) field must be assigned a value exactly once by the time the instance initialization is complete. This means there are three places where you can initialize one (but you must pick one):

1. Definition line:

// For when you know the answer
class Foo {
    final int theAnswer = 42;
}

2. Constructor:

// For when you need to have the answer passed in
class Foo {
    final int theAnswer;

    Foo(int answer) {
        theAnswer = answer;
    }
}

// Or for when you need to do some computation
class Bar {
    static final int ANSWER_COUNT = 10;
    final int[] answers;

    Foo() {
        answers = new int[ANSWER_COUNT];
        for (int i = 0; i < ANSWER_COUNT; i++) {
            answers[i] = i;
        }
    }

3. Initializer block:

// For when you need to do some computation and have many constructors
class Bar {
    static final int ANSWER_COUNT = 10;
    final int[] answers;

    {
        answers = new int[ANSWER_COUNT];
        for (int i = 0; i < ANSWER_COUNT; i++) {
            answers[i] = i;
        }
    }

    // I have many constructors and don't want to copy-paste 
    // the initialization logic above to all of them
    Bar() { ... }
    Bar(int i) { ... }
    Bar(String blah) { ... }
}

From the same page in the tutorial, regarding initializer blocks:

The Java compiler copies initializer blocks into every constructor. Therefore, this approach can be used to share a block of code between multiple constructors.

Upvotes: 2

duffymo
duffymo

Reputation: 308763

You need to assign a value when it's declared - in the constructor if it's not static, in the static initializer block if it is. Once you set the value, it can't be modified.

Do it like this:

public class FinalTest {
    private static final String CONSTANT;

    private final String value;

    static {
        CONSTANT = "Hello";
    }

    public static void main(String [] args) {
        FinalTest ft = ((args.length > 0) ? new FinalTest(args[0]) : new FinalTest(CONSTANT));
        System.out.println(ft);
    }

    public FinalTest(String value) {
        this.value = value;
    }

    public String toString() { return this.value; }
}

Upvotes: 3

Paul Blessing
Paul Blessing

Reputation: 3845

In the code you posted, the field is static so it could be given a value from within a static initialization block:

static {
    str = "my string";
}

For non-static fields, they can either be initialized in the constructor, or in an instance initializer block:

class TestClass {
    private final String str;

    {
        str = "my string";
    }

    TestClass() {

    }
}

Upvotes: 1

Related Questions