Не может ссылаться на "X" до того, как был вызван конструктор супертипа, где x - конечная переменная

Рассмотрим следующее объявление класса Java:

public class Test {

    private final int defaultValue = 10;
    private int var;

    public Test() {
        this(defaultValue);    // <-- Compiler error: cannot reference defaultValue before supertype constructor has been called.
    }

    public Test(int i) {
        var = i;
    }
}

Код не будет компилироваться, при этом компилятор жалуется на строку, выделенную выше. Почему эта ошибка происходит и что самое лучшее обходное решение?

Ответ 1

Причина, по которой код изначально не компилируется, заключается в том, что defaultValue является переменной экземпляра класса Test, что означает, что когда создается объект типа Test, уникальная экземпляр defaultValue также создается и прикрепляется к этому конкретному объекту. Из-за этого невозможно ссылаться на defaultValue в конструкторе, поскольку ни он, ни объект еще не созданы.

Решение состоит в том, чтобы сделать конечную переменную static:

public class Test {

    private static final int defaultValue = 10;
    private int var;

    public Test() {
        this(defaultValue);
    }

    public Test(int i) {
        var = i;
    }
}

Сделав переменную static, она становится ассоциированной с самим классом, а не с экземплярами этого класса и разделяется между всеми экземплярами Test. Статические переменные создаются, когда JVM сначала загружает класс. Поскольку класс уже загружен, когда вы используете его для создания экземпляра, статическая переменная готова к использованию и поэтому может использоваться в классе, включая конструктор.

Литература:

Ответ 2

Это потому, что defaultValue является членом экземпляра Test, который находится в разработке (еще не создан)

Если у вас есть это static, он был загружен, когда ваш класс загружается загрузчиками классов

Ответ 3

Правило: каждый конструктор должен выполнить конструктор суперкласса перед тем, как выполнить его.

Итак, первой строкой каждого конструктора является super() или может быть this(), и вы отправляете defaultValue этому конструктору класса, который (defaultValue) еще не существует, поэтому есть ошибка времени компиляции.

Вы можете сделать defaultValue статичным и, поскольку статическая переменная создается, поскольку класс загружается в память, поэтому defaultValue доступен в строке this (defaultValue).

Ответ 4

Пока ваш объект не будет создан, значения по умолчанию для переменных не будут установлены, поэтому если вы хотите, чтобы их значения по умолчанию были установлены во время построения, сделайте их static или явно задайте их раньше.

Ответ 5

Конструктор

вызывается во время создания объекта, поэтому никакая ссылка на varable не распознается компилятором, поскольку компилятор не знает о переменной экземпляра, поскольку объект еще не создан.

Ответ 6

в этом разделе я следил за этим.

class Super{

    int a=10;
    static int x=100;

    Super(){
        System.out.println("Super()");
    }
    Super(int i){
        System.out.println("Super(int)");
    }   
}

class Sub extends Super{
    int b=20;
    static int y=20;
    Sub(int i){
        ***Super(b);***
        System.out.println("Sub(int)");
    }
}

переменные экземпляра супер и подклассов, мы не можем использовать для ссылки. Поскольку переменные экземпляра инициализируются после запуска конструктора. И переменные не являются окончательными и статическими. Значения могут быть изменены. Как и эти только переменные экземпляра, поэтому мы создаем объекты для инициализации этих переменных. Поэтому эти переменные не могут использоваться для ссылок. Но если эти переменные являются окончательными и статичными, они остаются только в шаблоне. Как и эти переменные имеют определенные значения, и они не изменяются. Поэтому мы можем вызвать конструкторы суперклассов, используя эти статические конечные переменные.

Ответ 7

На самом деле это неправильный ответ, так как при создании объекта выполняются инструкции инициализации полей перед конструктором. Вы можете отлаживать процесс создания объекта и видеть это самостоятельно. Я сам тоже смущен этой проблемой.. например, если вы измените бит, а первый конструктор будет:

public Test(int i) {
   this(i, 0);
}
public Test (int a, int k) {
}

Это сработает.. поэтому, когда первый/пустой конструктор вызывает другой, он не работает по какой-то странной причине, даже если я явно вызываю super(); раньше.

Наиболее подходящим объяснением было бы то, что JVM загружает объявления в память, но NO CONSTRUCTOR НЕ ДОЛЖЕН ПОЛУЧИТЬ ЛЮБОЙ ИНТЕНСИВНЫЙ ПЕРЕМЕННЫЙ /FIELD , прежде чем он будет полностью выполнен.

Ответ 8

вы ссылаетесь на переменную, которая dosen't существует еще, если он был статическим, поэтому он будет существовать еще до самого конструктора

но вы столкнетесь с другой проблемой, поскольку defaultValue стал статичным, поэтому все остальные экземпляры могут использовать одно и то же значение, которое вам может не нравиться,

public class Test {

    private final int defaultValue = 10; //this will be exists only after calling the contractor
    private final static int vakue2= 10; //this is exists before the contractor has been called
    private int var;

    public Test() {
       // this(defaultValue);    // this metod will not work as defaultValue doesn't exists yet
    this(value2); //this will work
    //this(10); will work
    }

    public Test(int i) {
        var = i;
    }
}