web-dev-qa-db-fra.com

Impossible de faire référence à "X" avant l'appel du constructeur de supertype, où x est une variable finale

Considérez la déclaration de classe Java suivante:

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;
    }
}

Le code ne sera pas compilé, le compilateur se plaignant de la ligne que j'ai mise en évidence ci-dessus. Pourquoi cette erreur se produit-elle et quelle est la meilleure solution de contournement?

60
Amr Bekhit

La raison pour laquelle le code n'aurait pas initialement été compilé est parce que defaultValue est une variable instance de la classe Test, ce qui signifie que lorsqu'un objet de type Test est créé, une instance unique de defaultValue est également créée et attachée à cet objet. . De ce fait, il n'est pas possible de faire référence à defaultValue dans le constructeur, car ni celui-ci ni l'objet n'ont encore été créés.

La solution consiste à créer la variable finale static:

public class Test {

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

    public Test() {
        this(defaultValue);
    }

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

En rendant la variable static, elle devient associée à la classe elle-même plutôt qu'à des instances de cette classe et est partagée entre toutes les instances de Test. Les variables statiques sont créées lors du premier chargement de la classe par la machine virtuelle Java. Comme la classe est déjà chargée lorsque vous l'utilisez pour créer une instance, la variable statique est prête à être utilisée et peut donc être utilisée dans la classe, constructeur compris.

Références:

84
Amr Bekhit

C'est parce que defaultValue est un membre de l'instance de Test qui est en construction (pas encore créé) 

Si vous l’aviez static, il était chargé lors du chargement de votre classe par les chargeurs de classes

8
Jigar Joshi

Règle: chaque constructeur doit exécuter le constructeur de la super classe avant de s'exécuter lui-même.

Ainsi, la première ligne de chaque constructeur est super () ou peut être this () et vous envoyez la valeur defaultValue au constructeur this class qui (defaultValue) n’existe pas encore, ce qui entraîne une erreur de compilation.

Vous pouvez définir defaultValue en tant que variable statique et, puisque la variable statique est créée en tant que classe est chargée en mémoire, ainsi, defaultValue est disponible sur la ligne this (defaultValue).

4
Himanshu Mohta

vous faites référence à une variable qui n'existe pas encore , si elle était statique donc il existera même avant le constructeur lui-même  

mais vous ferez face à un autre problème, car defaultValue est devenu statique, de sorte que toutes les autres instances peuvent partager la même valeur, ce qui peut ne pas vous plaire,

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;
    }
}
2
Sherif Eldeeb

Tant que votre objet n’est pas construit, les valeurs par défaut des variables ne seront pas définies. Par conséquent, si vous souhaitez que leurs valeurs par défaut soient définies au moment de la construction, définissez-les static ou les définissez explicitement auparavant.

1
amicngh

le constructeur est appelé au moment de la création de l'objet; aucune compo-.

0
Hardik Mehta

En réalité, ce n'est pas une réponse correcte, car lors de la création d'un objet, les instructions d'initialisation des champs sont exécutées avant le constructeur. Vous pouvez déboguer le processus de création d'objet et le voir vous-même. Je suis moi-même confus à propos de ce problème aussi .. par exemple, si vous voulez changer un peu et que le premier constructeur sera:

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

Cela fonctionnera .. donc, lorsque le premier constructeur/null en appelle un autre, il ne fonctionne pas pour une raison étrange, même si j'appelle explicitement super (); avant.

L’explication la plus pertinente est que JVM charge les déclarations en mémoire, mais que NO CONSTRUCTOR IS ne peut atteindre aucun variable/champ avant son exécution complète.

0
Giorgi Tsiklauri