Maciej Ziarko
Maciej Ziarko

Reputation: 12084

Splitting a constructor method into parts - the problem with final values

I wanted to split the constructor of my class into parts. But I have a problem...

Is it possible to initialize a final value in a method called inside constructor? It has to be initialized directly in constructor?

This...

import java.util.Scanner;

public final class A
{
    private final int L;
    private final int D;
    private final int N;

    public A()
    {
        Scanner scanner = new Scanner(System.in);
        this.getFirstLine(scanner);

        /* the rest of the constructor method */
    }

    private void getFirstLine(Scanner scanner)
    {
        this.L = scanner.nextInt();
        this.D = scanner.nextInt();
        this.N = scanner.nextInt();
    }
}

gives me errors similar to The final field A.L cannot be assigned.

So it is treated as an assignment? Yeah?

Is there a way of splitting constructor in order to achieve what I wanted?

Thanks in advance.

Upvotes: 3

Views: 1886

Answers (5)

Maciej Ziarko
Maciej Ziarko

Reputation: 12084

OK, that's what I've done after being inspired by answers:

import java.util.Scanner;

public final class A
{
    private static final Scanner scanner = new Scanner(System.in);

    private static class FirstLine
    {
        public static int[] get()
        {
            return new int[]
            {
                A.scanner.nextInt(), 
                A.scanner.nextInt(), 
                A.scanner.nextInt()
            };
        }
    }

    private final int L;
    private final int D;
    private final int N;

    public A()
    {
        int[] valuesScanned = FirstLine.get();
        this.L = valuesScanned[0];
        this.D = valuesScanned[1];
        this.N = valuesScanned[2];
    }

    /*JUST TO TEST*/
    public static void main(String[] args)
    {
        A test = new A();
        System.out.println(test.L);
        System.out.println(test.D);
        System.out.println(test.N);
    }
}

I've made private static class that is responsible for reading input. Now it should be more logically divided into separate parts.

Upvotes: 0

Stephen C
Stephen C

Reputation: 718758

Is it possible to initialize a final value in a method called inside constructor?

No it is not possible.

It has to be initialized directly in constructor?

It has to be initialized by the end of each and every constructor. The initialization can be done in the declaration or an instance initializer ... that gets executed as part of all constructors.

So it is treated as an assignment? Yeah?

Yeah!

The problem is that the method could be called at any point:

  • it could be called twice by a constructor,
  • it could be called after the object has been constructed, or
  • it could throw an exception before / after assignment, leaving it unclear whether the assignment occurred. (And the constructor could catch the exception ...)

Rather than requiring the compiler to do a complicated analysis to ensure the assignment happens once and only once during construction (and not after construction), the JLS simply forbids the assignment in that context.

Is there a way of splitting constructor in order to achieve what I wanted?

You can calculate the values in a separate method, but the actual initialization of the final must be performed in the constructors, the declaration initializer or an initializer block.

(If there were subclasses in the frame, there are other tricks that could possibly be used.)

Upvotes: 3

Prince John Wesley
Prince John Wesley

Reputation: 63688

How about this.

import java.util.Scanner;

public final class A {

    private final int l;
    private final int d;
    private final int n;

    public A() {
        Scanner scanner = new Scanner(System.in);
        int[] list = this.getFirstLine(scanner);
        l = list[0];
        d = list[1];
        n = list[2];

        /* the rest of the constructor method */
    }

    private int[] getFirstLine(Scanner scanner) {
        return new int[]{scanner.nextInt(), scanner.nextInt(), scanner.nextInt()};
    }
}

Upvotes: 3

Asaph
Asaph

Reputation: 162781

You cannot do this according to the Java Language Spec:

A blank final instance variable must be definitely assigned (§16.9) at the end of every constructor (§8.8) of the class in which it is declared; otherwise a compile-time error occurs.

You can do it in the constructor because the compiler can guarantee that the constructor will only be called once per instance. The code below compiles. I just verified it:

import java.util.Scanner;

public final class A
{
    private final int L;
    private final int D;
    private final int N;

    public A()
    {
        Scanner scanner = new Scanner(System.in);
        this.L = scanner.nextInt();
        this.D = scanner.nextInt();
        this.N = scanner.nextInt();

        /* the rest of the constructor method */
    }
}

You cannot assign the final variables in your non-constructor method because it cannot be guaranteed to be called only once per instance.

Upvotes: 3

krock
krock

Reputation: 29619

final members can only be set during initialisation of the instance. So you can initialize final fields when you declare it, in an initialization block or in a constructor. Why don't you make getFirstLine a private constructor:

private A(Scanner scanner)
{
    this.L = scanner.nextInt();
    this.D = scanner.nextInt();
    this.N = scanner.nextInt();
}

Upvotes: 2

Related Questions