1

I was preparing for Java OCA certification where I came across a similar question like that of the following one:

public class TestClass {
    final int i;

    public static void main(String[] args) {
        TestClass t = new TestClass();
        System.out.println(t.i);
    }
}

As per Java, the instance and static variables are given default values. Surprisingly, this was error that was shown in my console:

error: variable i not initialized in the default constructor final int i;

Why wasn't i assigned a default value above?

Andrew Li
  • 55,805
  • 14
  • 125
  • 143
scriobh
  • 868
  • 10
  • 25
  • what value would expect it to get? If a class has a final member, it has to be initialized in the constructor explicitly. – Arthur Cinader Apr 11 '17 at 20:16
  • 1
    Thought that it would display 0 as the default value of a variable of type int is 0. @ArthurCinader – scriobh Apr 11 '17 at 20:17

5 Answers5

2

Assigning default value to the final variable would defeat the entire purpose of making a variable final in the first place.
final would mean that you can't change the value once assigned. If a default value is given to final variable then you would never be able to set the value of the variable to something else (even for the first time).

RITZ XAVI
  • 3,633
  • 1
  • 25
  • 35
2

It's defined this way in the Java Language Specification:

Chapter 16. Definite Assignment

Each local variable (§14.4) and every blank final field (§4.12.4, §8.3.1.2) must have a definitely assigned value when any access of its value occurs.

[...]

For every access of a local variable or blank final field x, x must be definitely assigned before the access, or a compile-time error occurs.

Where the term "blank final field" refers to a final field with no value or initializer, and "definitely assigned" means that the field will be assigned to no matter what:

The idea behind definite assignment is that an assignment to the local variable or blank final field must occur on every possible execution path to the access. Similarly, the idea behind definite unassignment is that no other assignment to the blank final variable is permitted to occur on any possible execution path to an assignment.

Aside from throwing an error by specification, there's logical reasoning behind the decision. There's no point in having a default value for a blank final field. In your case, the blank final is an integer, and it would just be given 0 and you wouldn't be able to change it. What would the use of the variable be?

Also, if a final variable is not explicitly given a default value, why not initialize it in the first place? You can't reassign to it later since a default value would already be given, so why not initialize it now?

Community
  • 1
  • 1
Andrew Li
  • 55,805
  • 14
  • 125
  • 143
1

Error message is misleading since it can be initialized with default value:

class FinalTest{
    final int x = printXAndInitializeIt();

    private int printXAndInitializeIt(){
        System.out.println("x before initialization = "+x);
        return 1;
    }

    public static void main(String[] args) {
        FinalTest ft = new FinalTest();
        System.out.println("x after initialization = "+ft.x);
    }
}

Output:

x before initialization = 0
x after initialization = 1

But even if there is default value final variable expects explicit initialization, since in most cases lack of initialization is caused by mistake. This error could be changed to warning, but I am guessing that Java designers though that "better safe than sorry".

Pshemo
  • 122,468
  • 25
  • 185
  • 269
  • 1
    Interesting quirk of initialization is that even non-`final` member variables get initialized twice if initialized explicitly, once to the default, and then to the explicit value. So `private int index = 0;` initializes the variable to zero twice. The JVM is forbidden to optimize that to once. – Lew Bloch Apr 11 '17 at 20:37
  • 1
    @LewBloch where did you get that forbiddance from? – Holger Sep 16 '22 at 17:59
  • @Holger I would guess that it follows from the possibility that a different class in the inheritance hierarchy might be able to assign a value to the field (e.g. via reflection, or by calling a method on the class which has access to the field) before its initialiser is supposed to be evaluated. So e.g. if the initialiser said `index = 5` but something assigned `23` before that, the end result should be `5`; and the same must apply if the initialiser says `index = 0`. – kaya3 Sep 16 '22 at 23:01
  • Example: `class Foo { Foo() { ((Bar) this).x = 23; } }`, `class Bar extends Foo { int x = 0; }`. The `Foo` constructor is invoked before the initialiser in `Bar`, so `x` receives its initial default of `0`, then is assigned `23`, and then is assigned `0`. – kaya3 Sep 16 '22 at 23:04
  • 1
    @kaya3 well, when there is an action between the write of the default value and the initialization, the behavior must be retained. But that’s a different thing than saying that this redundant assignment can not get optimized. E.g., in your example, all three assignments still can be fold into one. – Holger Sep 19 '22 at 06:45
  • @Holger From the JLS 8.3.2 "At run time, the initializer is evaluated and the assignment performed exactly once, when the class is initialized". In 12.3.2 it says "Preparation involves creating the static fields (class variables and constants) for a class or interface and initializing such fields to the default values (§4.12.5). This does not require the execution of any source code; explicit initializers for static fields are executed as part of initialization (§12.4), not preparation." The preparation must set the field to its default first. – Lew Bloch Feb 18 '23 at 23:00
  • 1
    @LewBloch this describes *observable* behavior. At no point does this forbid optimizations. Optimizations just have to maintain the mandated behavior, as far as observable (as always). If there’s no read between these mandates writes (as in most cases), there is no way to observe a difference. – Holger Feb 20 '23 at 08:01
  • @Holger because the class load and initialization steps are separate, it is inevitable that static variables get loaded with their zero-like values before the initialization runs. Only upon initialization do those code values get set. So for an indeterminate time the static variables hold their zero-like values. – Lew Bloch Feb 23 '23 at 00:01
  • 1
    @LewBloch static variables are not loaded. The memory to hold their values is not part of the class file format. And since nothing can read those variables before the initializer, there is no requirement to allocate that memory before the initialization starts. However, [your first comment](https://stackoverflow.com/questions/43355406/43355587?noredirect=1#comment73774657_43355587) isn’t even about static variables. – Holger Feb 23 '23 at 07:23
0

Why didn't it assign a default value in the above case ? Can anyone explain!

if a default value were to be provided then you wouldn't be able to change the value of it. when the final keyword is used with variables it means once the variable is assigned, it cannot be re-assigned.

Wikipedia:

A final variable can only be initialized once, either via an initializer or an assignment statement. It does not need to be initialized at the point of declaration: this is called a "blank final" variable. A blank final instance variable of a class must be definitely assigned in every constructor of the class in which it is declared; similarly, a blank final static variable must be definitely assigned in a static initializer of the class in which it is declared; otherwise, a compile-time error occurs in both cases. (Note: If the variable is a reference, this means that the variable cannot be re-bound to reference another object. But the object that it references is still mutable, if it was originally mutable.)

further reading:

Definite Assignment

Ousmane D.
  • 54,915
  • 8
  • 91
  • 126
  • See the JLS for details of _definitely [un]assigned_. – Lew Bloch Apr 11 '17 at 20:33
  • Final fields *do* have default values which can be visible before they are assigned; see the second example [here](https://stackoverflow.com/q/24990691/12299000). – kaya3 Sep 16 '22 at 15:55
  • 1
    @kaya3 you can prove that even easier: `final int a = this.b, b = "x".length();` – Holger Sep 16 '22 at 18:05
0

It's not that final fields aren't initialized to the default value — in fact they sort of are, though you'll only observe the fact if you're doing some silly stuff — but that you're required to explicitly initialize them exactly once (either as part of the declaration, or in an initializer block, or in every single constructor).

To understand the motivation for this, consider the following code:

public class Foo {
    private final int mValue;

    public Foo(final boolean shouldSet) {
        if (shouldSet) {
            mValue = 1;
        }
    }

    // ...
}

If the field weren't final, then the compiler would infer an implicit = 0 at the end of its declaration; but with a final field that's not always valid . . . and in the above example, the compiler can't even tell beforehand whether it will be valid or not. So the compiler sidesteps the issue by never inferring an implicit = 0, and requiring explicit initialization.

ruakh
  • 175,680
  • 26
  • 273
  • 307