Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Initializing a final variable in a method called from class constructor [duplicate]

Today I'm facing a strange behavior which I couldn't figure out why.

Imagine we have a final variable in a typical class in Java. We can initialize it instantly or in class constructor like this:

public class MyClass {

    private final int foo;

    public MyClass() {
        foo = 0;
    }
}

But I don't know why we can't call a method in constructor and initialize foo in that method, like this:

public class MyClass {

    private final int foo;

    public MyClass() {
        bar();
    }

    void bar(){
        foo = 0;
    }
}

Because I think we are still in constructor flow and it doesn't finished yet. Any hint will be warmly appreciated.

like image 416
Alireza Avatar asked Mar 12 '18 13:03

Alireza


People also ask

Can we initialize final variable using constructor?

A blank final variable can be initialized inside an instance-initializer block or inside the constructor. If you have more than one constructor in your class then it must be initialized in all of them, otherwise, a compile-time error will be thrown.

Can we initialize final variable in main method?

A final variable can be initialized only once. A final variable at class level must be initialized before the end of the constructor.

Do we need to initialize final variable?

Declaring final variable without initialization If you declare a final variable later on you cannot modify or, assign values to it. Moreover, like instance variables, final variables will not be initialized with default values. Therefore, it is mandatory to initialize final variables once you declare them.

Which constructor is used for initializing an object through another object?

A copy constructor is a member function that initializes an object using another object of the same class.


3 Answers

First, assigning the value at declaration time is copied into every constructor for you by the compiler. Second, you can use a method to initialize the value, but you need to return it for that to work. As others' note, you are required to ensure this value is set once.

public class MyClass {
    private final int foo = bar();

    private static int bar() {
        return 0;
    }
}

Which is equivalent to

public class MyClass {
    private final int foo;

    public MyClass() {
        this.foo = bar();
    }

    private static int bar() {
        return 0;
    }
}

Note that bar is static, because otherwise you need an instance to call it.

like image 185
Elliott Frisch Avatar answered Oct 17 '22 09:10

Elliott Frisch


You can only initialize a final variable once. There are three forms of final variables:

  • class final variables
  • Instance final variables
  • Local final variables.

For class final variables, the variables can be initialized in either the declaration or static initializer:

class Program {
static final int i1 = 10;
static final int i2;
static {
    i2 = 10;
}

}

For instance final variables, the variables can be initialized in the declaration, instance initializer, or constructor:

class Program {
final int i1 = 10;
final int i2;
final int i3;
{
    i2 = 10;
}

Program() {
    i3 = 10;
}

}

For local final variables, the variables can be initlialized in the declaration or any place after its declaration. The local final variables must be initialized before they are used.

class Program {
void method() {
     final int i1 = 10;
     final int i2;
     System.out.println(i1);
     i2 = 10;
     System.out.println(i2);
     return ;
}

}

Source: Refer link Reference Link

like image 22
Subash J Avatar answered Oct 17 '22 08:10

Subash J


Final modifier on field (or variable) means that compiler will ensure that both of the following are true:

  • The field is initialized at least once during object construction, unless object construction fails.
  • The field is initialized at most once.

For your code, none of those is guaranteed:

  • Some subclass might override method bar.
  • Some other class in the same package might call method bar once more.

It might be tempting to use private method rather than package-private. While it could guarantee you both of those conditions (unless you try to break it by reflection), javac still will not accept it, because it is not so powerful. There are some good reasons:

  1. First, it has to have some limits. If the compiler was able to fully decide if both conditions are satisfied, it would be able to decide halting problem, which is not possible. So, some reasonable subset was chosen.
  2. Imagine it is able to detect this particular situation. This means you have some private method that has to be called from constructor and not from elsewhere. In such cases, programmer would need a descriptive error message why such a private method (that looks like a normal method at first sight) cannot be called here. Later, someone would create some monster method that conditionally assigns to the final field. For some complex-enough conditions, javac would be unable to find out if it assigns to the final field or not, so someone would face some mysterious error message. I don't think it is easy to make a good error message in such situation.
  3. Calling private instance methods is already tricky. As the method will operate on a not-fully-initialized object, so it can, for example, read some uninitialized (even final) properties.
  4. I believe that constructor should be rather short and simple, usually just assigning parameters to fields, plus some validation. If things are getting complex, you might want to create a factory method. Object creation would become clearly separated from object methods, which is not the case when you need to call a private method.

Instead of such mouse-cat-hunt, the language designers have decided to support just some well understood cases. In other cases, the code can be probably refactored. So, language creators can focus on some more important aspects.

like image 37
v6ak Avatar answered Oct 17 '22 09:10

v6ak