54 votes

Utilisation du champ final non initialisé - avec / sans 'ceci.' qualificatif

Quelqu'un peut m'expliquer pourquoi la première des deux échantillons compile, tandis que le second ne l'est pas? Avis la seule différence est que le premier qualifie explicitement la référence à x".ce, tandis que la seconde ne l'est pas. Dans les deux cas, le dernier champ x est clairement la tentative d'usage avant d'initialisation.

J'aurais pensé que les deux échantillons devrait être traitée complètement égale, résultant en une erreur de compilation pour les deux.

1)

public class Foo {
    private final int x;
    private Foo() {
        int y = 2 * this.x;
        x = 5;
    }
}

2)

public class Foo {
    private final int x;
    private Foo() {
        int y = 2 * x;
        x = 5;
    }
}

39voto

ruakh Points 68789

Après un tas de spec de la lecture et de la pensée, j'en ai conclu que:

Dans une Java 5 ou Java 6 compilateur, c'est le comportement correct. Chapitre 16 "Certaine Affectation de La Java Language Specification, Troisième Édition , dit:

Chaque variable locale (§14.4) et tous les espaces final (§4.12.4) domaine (§8.3.1.2) doit avoir un certainement affecté de la valeur lors de tout accès de sa valeur se produit. Un accès à sa valeur consiste en la simple nom de la variable n'importe où dans une expression, sauf que la gauche opérande de la simple opérateur d'assignation =.

(l'emphase est mienne). Ainsi, dans l'expression 2 * this.x, this.x partie n'est pas considéré comme un "accès de [xs'] valeur" (et n'est donc pas soumis aux règles d'une certaine affectation), car this.x n'est pas le simple nom de la variable d'instance, x. (N. B. la règle lors de l'affectation définitive se produit, dans le paragraphe après la citée ci-dessus le texte, ne permettent quelque chose comme this.x = 3, et considère x définitivement d'être affectées par la suite; c'est seulement la règle pour les accès qui ne compte pas this.x.) Notez que la valeur de this.x dans ce cas sera de zéro, par §17.5.2.

Dans Java 7 compilateur, c'est un bug du compilateur, mais compréhensible. Chapitre 16 "Certaine" d'Assignation de la Java Language Specification, Java 7 Édition SE dit:

Chaque variable locale (§14.4) et tous les espaces final domaine (§4.12.4, §8.3.1.2) doit certainement affecté de la valeur lors de tout accès de sa valeur se produit.

Un accès à sa valeur consiste en la simple nom de la variable (ou, pour un champ, le simple nom de domaine qualifié en this) n'importe où dans une expression, sauf que la gauche opérande de la simple opérateur d'assignation = (§15.26.1).

(l'emphase est mienne). Ainsi, dans l'expression 2 * this.x, this.x partie doit être considérée comme un "accès à l' [xs'] valeur", et devrait donner une erreur de compilation.

Mais vous ne vous demandez pas si le premier devrait compiler, avez-vous demandé pourquoi il ne la compilation (dans certains compilateurs). Ce n'est pas nécessairement spéculative, mais je vais faire deux suppositions:

  1. La plupart des compilateurs Java 7 ont été écrits par la modification de la version 6 de Java compilateurs. Certains compilateur écrivains peuvent ne pas avoir remarqué ce changement. En outre, de nombreux Java-7 compilateurs et des IDEs encore le support de Java 6, et certains compilateur écrivains peuvent ne pas avoir senti motivé pour spécifiquement rejeter quelque chose en Java-7 mode qu'ils acceptent en Java-6 mode.
  2. Le nouveau Java 7 comportement est étrange incohérence. Quelque chose comme (false ? null : this).x , est encore autorisé, et pour cette question, de même (this).x , est encore autorisé; c'est seulement le jeton de la séquence de this plus . plus le nom de zone qui est affecté par ce changement. Accordé, une telle incohérence existait déjà sur le côté gauche d'une instruction d'affectation (on peut écrire this.x = 3, mais pas (this).x = 3), mais c'est plus facilement compréhensible: c'est en acceptant this.x = 3 spéciale du permis de cas de l'autre interdit la construction obj.x = 3. Il est logique de le faire. Mais je ne pense pas que cela a du sens pour rejeter 2 * this.x spécial interdit de cas de la par ailleurs permis la construction 2 * obj.x, étant donné que (1) cette spéciale interdit de cas est facilement contourné en ajoutant entre parenthèses, que (2) cette spéciale interdit de cas a été autorisé dans les versions précédentes de la langue, et que (3) nous avons encore besoin de la règle spéciale en vertu duquel final les champs ont des valeurs par défaut (par exemple, 0 d'un int) jusqu'à ce qu'ils sont initialisés, à la fois parce que des cas comme (this).x, et parce que des cas comme this.foo()foo() est une méthode qui accède x. Ainsi, certains compilateur écrivains peuvent ne pas avoir senti motivé pour faire cette incompatible changement.

L'un de ces serait étonnant — je suppose que le compilateur écrivains avaient des informations détaillées sur chaque changement de la spécification, et dans mon expérience, compilateurs Java sont généralement assez bon pour coller à la spécification exactement (contrairement à d'autres langues, où chaque compilateur a son propre dialecte) — mais, bien, quelque chose est arrivé, et le ci-dessus sont mes deux seules des suppositions.

2voto

Yogendra Singh Points 19406

Lorsque vous utilisez this dans le constructeur, le compilateur est de voir x comme un attribut de membre de l' this objet (par défaut initialisé). Depuis x est int, par défaut c'est initialisée avec 0. Cela rend compilateur heureux et sa fonctionne très bien au moment de l'exécution.

Lorsque vous n'utilisez pas l' this, alors le compilateur utilise x déclaration directement dans l'analyse lexicale et par conséquent, il se plaint, c'est d'initialisation (moment de la compilation phénomène).

C'est donc la définition de l' this, ce qui rend compilateur pour analyser x comme une variable de membre d'un objet en rapport directe de l'attribut lors de l'analyse lexicale dans la compilation et résultant dans différents compilation comportement.

Lorsqu'il est utilisé comme primaire d'expression, le mot-clé this désigne une valeur qui est une référence à l'objet pour lequel la méthode d'instance a été invoquée (§15.12), ou à l'objet en cours de construction.

-1voto

Slauster Points 99

Je pense que le compilateur estime que l'écriture de this.x implique que "cela" existe, donc un constructeur a été appelé (et la variable finale a été initialisée). Mais vous devriez obtenir une exception RuntimeException lorsque vous essayez de l'exécuter.

-1voto

Andy Points 1094

Je suppose que vous faites référence au comportement dans Eclipse. (Comme indiqué comme commentaire, une compilation avec des travaux javac).

Je pense que c'est un problème Eclipse. Il a son propre compilateur et son propre ensemble de règles. L'une d'elles est que vous ne pouvez pas accéder à un champ qui n'est pas initialisé, bien que Java-Commpiler initialise les variables pour vous.

-1voto

jWeaver Points 4191

Selon la Spécification du Langage Java

Tous les espaces dernière variable doit être attribuée qu'une fois au plus; il doit être certainement non attribuées lors d'une cession à il se produit. Une telle l'affectation est définie de se produire si et seulement si le simple nom de de la variable (ou, pour un champ, son simple nom qualifié par cet) se produit sur le côté gauche d'un opérateur d'affectation.

Cas 1:

public class Foo {
    private final int x;
    private Foo() {
        int y = 2 * this.x;
        x = 5;
    }
}

Depuis, nous voici à l'aide d' this qui se réfèrent à courant constructeur de la classe. Ce qui signifie, il va appeler le courant constructeur de la classe et de l'initialiser tous c'variable d'instance avec leur valeur par défaut.

Cas 2:

public class Foo {
    private final int x;
    private Foo() {
        int y = 2 * x;   // compile error
        int y = 2 * (new Foo()).x; // no error
        x = 5;
    }
}

Maintenant, dans ce cas, nous sommes multiplier x qui n'a pas été initialiser jusqu'à maintenant. Même, vous pouvez remarquer ce, dans NetBeans, il est dit x n'est pas initialisé. Donc, si vous remplacez l' int y = 2 * x; de int y = 2 * (new Foo()).x;. Ensuite, il n'affiche pas d'erreur.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X