Utilisation du champ final non initialisé – avec / sans ‘this’ qualificatif

Quelqu’un peut-il m’expliquer pourquoi le premier des deux échantillons suivants est compilé, alors que le second ne le fait pas? Notez que la seule différence est que le premier qualifie explicitement la référence à x avec ‘.this’, tandis que le second ne le fait pas. Dans les deux cas, le champ final x est clairement tenté d’être utilisé avant d’être initialisé.

J’aurais pensé que les deux échantillons seraient traités de la même manière, ce qui entraînerait 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; } } 

Après un tas de lectures et de reflections, j’ai conclu que:

Dans un compilateur Java 5 ou Java 6, le comportement est correct. Chapitre 16 “Affectation définitive de la spécification de langage Java , troisième édition :

Chaque variable locale (§14.4) et chaque champ final blanc (§4.12.4) (§8.1.1.2) doit avoir une valeur clairement affectée lors de tout access de sa valeur. Un access à sa valeur consiste en un nom simple de la variable apparaissant n’importe où dans une expression, sauf en tant qu’opérande de gauche de l’opérateur d’affectation simple = .

(emphase le mien). Donc, dans l’expression 2 * this.x , la partie this.x n’est pas considérée comme un “access à la valeur de [ x ]” (et n’est donc pas soumise aux règles d’affectation définie), car this.x n’est pas la nom simple de la variable d’instance x . (NB: la règle pour l’affectation définitive, dans le paragraphe après le texte cité ci-dessus, autorise quelque chose comme this.x = 3 , et considère que x doit être définitivement atsortingbué par la suite; seule la règle pour les access ne compte pas this.x ) Notez que la valeur de this.x dans ce cas sera zéro, conformément au § 17.5.2 .

Dans un compilateur Java 7, il s’agit d’un bogue de compilation, mais compréhensible. Le chapitre 16 “Affectation définitive” de la spécification de langage Java, Java 7 SE Edition, indique:

Chaque variable locale ( §14.4 ) et chaque champ final vide ( §4.12.4 , §8.3.1.2 ) doivent avoir une valeur clairement affectée lors de tout access de sa valeur.

Un access à sa valeur se compose du nom simple de la variable (ou, pour un champ, du simple nom du champ qualifié par this ) apparaissant n’importe où dans une expression sauf en tant qu’opérande gauche de l’opérateur d’affectation simple = ( § 15.26.1 ).

(emphase le mien). Donc, dans l’expression 2 * this.x , la partie this.x devrait être considérée comme un “access à la valeur de [ x ]”, et devrait donner une erreur de compilation.

Mais vous ne vous êtes pas demandé si le premier devrait comstackr, vous avez demandé pourquoi il comstack (dans certains compilateurs). C’est nécessairement spéculatif, mais je vais faire deux conjectures:

  1. La plupart des compilateurs Java 7 ont été écrits en modifiant les compilateurs Java 6. Certains auteurs de compilateurs n’ont peut-être pas remarqué ce changement. De plus, de nombreux compilateurs et IDE Java-7 supportent toujours Java 6, et certains auteurs de compilateurs ne se sont peut-être pas sentis motivés pour rejeter spécifiquement quelque chose en mode Java-7 qu’ils acceptent en mode Java-6.
  2. Le nouveau comportement de Java 7 est étrangement incohérent. Quelque chose comme (false ? null : this).x est toujours autorisé, et même, même (this).x est toujours autorisé; c’est seulement la séquence de jetons spécifique à this plus . plus le nom de champ affecté par cette modification. Certes, 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 cela se comprend plus facilement: c’est accepter this.x = 3 tant que cas spécial autorisé de la construction autrement interdite obj.x = 3 . Il est logique de permettre cela. Mais je ne pense pas qu’il soit logique de rejeter 2 * this.x comme cas particulier interdit de la construction par ailleurs autorisée 2 * obj.x , étant donné que (1) ce cas spécial interdit est facilement contourné par l’ajout de parenthèses, (2) cette casse spéciale interdite était autorisée dans les versions précédentes du langage, et que (3) nous avons toujours besoin de la règle spéciale selon laquelle les champs final ont leurs valeurs par défaut ( 0 pour un int ) cas comme (this).x , et à cause de cas comme this.foo()foo() est une méthode qui accède à x . Ainsi, certains auteurs de compilateurs ne se sont peut-être pas sentis motivés pour effectuer ce changement incohérent.

L’une ou l’autre de ces options serait surprenante – je suppose que les auteurs de compilateurs avaient des informations détaillées sur chaque modification de la spécification, et d’après mon expérience, les compilateurs Java résistent généralement bien à la spécification (contrairement à certains langages). propre dialecte) – mais, bien, quelque chose s’est passé, et ce qui précède sont mes deux seules suppositions.

Lorsque vous l’utilisez dans le constructeur, le compilateur voit x comme un atsortingbut membre de this object (initialisé par défaut) . Puisque x est int , sa valeur par défaut est initialisée à 0 . Cela rend le compilateur heureux et son bon fonctionnement à l’exécution.

Lorsque vous ne l’utilisez pas, le compilateur utilise la déclaration x directement dans l’parsing lexicale et se plaint donc de son initialisation ( phénomène de compilation ).

Donc, c’est la définition de this , qui fait que le compilateur parsing x tant que variable membre d’un object par rapport à un atsortingbut direct lors de l’parsing lexicale dans la compilation et qu’il en résulte un comportement de compilation différent.

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

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

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

Je pense que c’est un problème Eclipse. Il possède 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-Commstackr initialise les variables pour vous.