Dans la portée de la méthode ou de la classe, la ligne ci-dessous compile (avec avertissement):
int x = x = 1;
Dans la portée de classe, où les variables obtiennent leurs valeurs par défaut , ce qui suit donne l'erreur `` référence indéfinie '':
int x = x + 1;
N'est-ce pas le premier qui x = x = 1
devrait se retrouver avec la même erreur de «référence indéfinie»? Ou peut-être que la deuxième ligne int x = x + 1
devrait être compilée? Ou il me manque quelque chose?
java
compiler-construction
Marcin
la source
la source
static
dans la variable class-scope, comme dansstatic int x = x + 1;
, obtiendrez-vous la même erreur? Parce qu'en C #, cela fait une différence si c'est statique ou non statique.static int x = x + 1
échoue en Java.int a = this.a + 1;
etint b = 1; int a = b + 1;
dans la portée de la classe (qui sont tous deux ok en Java) échouent, probablement en raison du §17.4.5.2 - "Un initialiseur de variable pour un champ d'instance ne peut pas référencer l'instance en cours de création." Je ne sais pas si c'est explicitement autorisé quelque part mais statique n'a pas une telle restriction. En Java , les règles sont différentes etstatic int x = x + 1
ne parvient pas pour la même raison que leint x = x + 1
faitRéponses:
tl; dr
Pour les champs ,
int b = b + 1
est illégal car ilb
s'agit d'une référence directe illégale àb
. Vous pouvez réellement résoudre ce problème en écrivantint b = this.b + 1
, qui compile sans se plaindre.Pour les variables locales ,
int d = d + 1
est illégal card
n'est pas initialisé avant utilisation. Ce n'est pas le cas pour les champs, qui sont toujours initialisés par défaut.Vous pouvez voir la différence en essayant de compiler
int x = (x = 1) + x;
comme déclaration de champ et comme déclaration de variable locale. Le premier échouera, mais le second réussira, en raison de la différence de sémantique.
introduction
Tout d'abord, les règles des initialiseurs de champ et de variable locale sont très différentes. Cette réponse abordera donc les règles en deux parties.
Nous utiliserons ce programme de test partout:
La déclaration de
b
n'est pas valide et échoue avec uneillegal forward reference
erreur.La déclaration de
d
n'est pas valide et échoue avec unevariable d might not have been initialized
erreur.Le fait que ces erreurs soient différentes devrait indiquer que les raisons des erreurs sont également différentes.
Des champs
Les initialiseurs de champs en Java sont régis par JLS §8.3.2 , Initialisation des champs.
La portée d'un champ est définie dans JLS §6.3 , Portée d'une déclaration.
Les règles pertinentes sont:
m
déclaré dans ou héritée par une classe de type C (§8.1.6) est le corps entier de C, y compris toutes les déclarations de type imbriquées.§8.3.2.3 dit:
Vous pouvez en fait faire référence à des champs avant qu'ils n'aient été déclarés, sauf dans certains cas. Ces restrictions visent à empêcher le code comme
de la compilation. La spécification Java indique que "les restrictions ci-dessus sont conçues pour intercepter, au moment de la compilation, les initialisations circulaires ou autrement mal formées".
À quoi se résument ces règles?
En bref, les règles disent essentiellement que vous devez déclarer un champ avant une référence à ce champ si (a) la référence est dans un initialiseur, (b) la référence n'est pas affectée à, (c) la référence est un nom simple (pas de qualificatif comme
this.
) et (d) il n'est pas accessible depuis une classe interne. Ainsi, une référence directe qui satisfait les quatre conditions est illégale, mais une référence directe qui échoue sur au moins une condition est OK.int a = a = 1;
compile parce qu'il viole (b): la référencea
est assignée, il est donc légal de s'y référera
avanta
la déclaration complète de.int b = this.b + 1
compile également car il viole (c): la référencethis.b
n'est pas un simple nom (il est qualifié parthis.
). Cette construction étrange est toujours parfaitement bien définie, car ellethis.b
a la valeur zéro.Donc, fondamentalement, les restrictions sur les références de champ dans les initialiseurs empêchent
int a = a + 1
une compilation réussie.Notez que la déclaration sur le terrain
int b = (b = 1) + b
va échouer à compiler, parce que la finaleb
est encore une référence avant illégale.Variables locales
Les déclarations de variables locales sont régies par JLS §14.4 , Instructions de déclaration de variables locales.
La portée d'une variable locale est définie dans JLS §6.3 , Portée d'une déclaration:
Notez que les initialiseurs sont dans la portée de la variable déclarée. Alors pourquoi ne
int d = d + 1;
compile pas ?La raison est due à la règle de Java sur l'affectation définie ( JLS §16 ). L'affectation définie indique essentiellement que chaque accès à une variable locale doit avoir une affectation précédente à cette variable, et le compilateur Java vérifie les boucles et les branches pour s'assurer que l'affectation se produit toujours avant toute utilisation (c'est pourquoi une affectation définie a une section de spécification entière dédiée à lui). La règle de base est:
x
,x
doit être définitivement attribué avant l'accès, ou une erreur de compilation se produit.Dans
int d = d + 1;
, l'accès àd
est résolu à la variable locale bien, mais commed
n'a pas été affecté avant l'd
accès, le compilateur émet une erreur. Dansint c = c = 1
,c = 1
se produit en premier, qui affectec
, puisc
est initialisé au résultat de cette affectation (qui est 1).Notez qu'en raison de règles d'affectation définies, la déclaration de variable locale
int d = (d = 1) + d;
sera compilée avec succès ( contrairement à la déclaration de champint b = (b = 1) + b
), car elled
est définitivement affectée au moment où la finaled
est atteinte.la source
int b = b + 1
b se trouve à droite (pas à gauche) de l'affectation, donc cela violerait cela ...int x = x = 1
, dans laquelle au cas où rien de tout cela ne s'appliquerait.est équivalent à
pendant que dans
nous devons d'abord calculer
x+1
mais la valeur de x n'est pas connue donc vous obtenez une erreur (le compilateur sait que la valeur de x n'est pas connue)la source
int x = x = 1;
équivaut àint x = (x = 1)
, nonx = 1; x = x;
. Vous ne devriez pas recevoir d'avertissement du compilateur pour cela.int x = x = 1;
s équivalent à int enx = (x = 1)
raison de l'associativité à droite de l'=
opérateurint x = (x = 1)
équivaut àint x; x = 1; x = x;
(déclaration de variable, évaluation de l'initialiseur de champ, affectation de variable au résultat de ladite évaluation), d'où l'avertissementC'est à peu près équivalent à:
Premièrement,
int <var> = <expression>;
équivaut toujours àDans ce cas, votre expression est
x = 1
, qui est également une déclaration.x = 1
est une instruction valide, puisque le varx
a déjà été déclaré. C'est aussi une expression avec la valeur 1, qui est ensuite affectée àx
nouveau.la source
0
valeur par défaut pour ints, donc je m'attendrais à ce que le résultat soit 1, pas leundefined reference
.x + 1
n'a pas de valeur définie, car ilx
n'est pas initialisé.x
est défini comme une variable membre ("dans la portée de la classe").En java ou dans n'importe quelle langue moderne, l'affectation vient de la droite.
Supposons que vous ayez deux variables x et y,
Cette déclaration est valide et c'est ainsi que le compilateur les divise.
Mais dans ton cas
Le compilateur a donné une exception car, il se divise comme ceci.
la source
int x = x = 1;
n'est pas égal à:javap nous aide à nouveau, ce sont des instructions JVM générées pour ce code:
plus comme:
Il n'y a aucune raison de lancer une erreur de référence non définie. Il y a maintenant une utilisation de variable avant son initialisation, donc ce code est entièrement conforme aux spécifications. En fait, il n'y a pas d'utilisation du tout de variable , juste des affectations. Et le compilateur JIT ira encore plus loin, il éliminera de telles constructions. Pour dire honnêtement, je ne comprends pas comment ce code est connecté à la spécification JLS d'initialisation et d'utilisation des variables. Aucune utilisation, aucun problème. ;)
Veuillez corriger si je me trompe. Je ne comprends pas pourquoi les autres réponses, qui font référence à de nombreux paragraphes JLS, rassemblent autant d'avantages. Ces paragraphes n'ont rien de commun avec ce cas. Juste deux affectations en série et rien de plus.
Si nous écrivons:
est égal à:
L'expression la plus à droite est simplement affectée aux variables une par une, sans aucune récursivité. Nous pouvons désordre les variables comme nous le souhaitons:
la source
Dans
int x = x + 1;
vous ajoutez 1 à x, donc quelle est la valeur dex
, il n'est pas encore créé.Mais in
int x=x=1;
compilera sans erreur car vous attribuez 1 àx
.la source
Votre premier morceau de code contient un deuxième
=
au lieu d'un plus. Cela compilera n'importe où tandis que le deuxième morceau de code ne se compilera à aucun endroit.la source
Dans le deuxième morceau de code, x est utilisé avant sa déclaration, tandis que dans le premier morceau de code, il est simplement attribué deux fois, ce qui n'a pas de sens mais est valide.
la source
Décomposons-le étape par étape, à droite associative
x = 1
, affectez 1 à une variable xint x = x
, assignez ce que x est à lui-même, comme un int. Étant donné que x était précédemment attribué à 1, il conserve 1, bien que de manière redondante.Cela compile bien.
x + 1
, ajoutez un à une variable x. Cependant, x étant non défini, cela provoquera une erreur de compilation.int x = x + 1
, ainsi cette ligne compile les erreurs car la partie droite des égaux ne compilera pas en ajoutant une à une variable non attribuéela source
=
opérateurs, donc c'est la même chose queint x = (x = 1);
.Le second
int x=x=1
est compilé car vous affectez la valeur au x mais dans les autres casint x=x+1
ici la variable x n'est pas initialisée, rappelez-vous en java les variables locales ne sont pas initialisées à la valeur par défaut. Remarque Si c'est (int x=x+1
) dans la portée de la classe également, cela donnera également une erreur de compilation car la variable n'est pas créée.la source
compile avec succès dans Visual Studio 2008 avec avertissement
la source
c
au lieu dejava
mais apparemment c'était l'autre question.bool y;
ety==true
retournera false.void main() { int x = x + 1; printf("%d ", x); }
dans Visual Studio 2008, dans Debug, j'obtiens l'exceptionRun-Time Check Failure #3 - The variable 'x' is being used without being initialized.
et dans Release, le numéro est1896199921
imprimé dans la console.static
champ (variable statique au niveau de la classe), les mêmes règles s'appliquent. Par exemple, un champ déclaré commepublic static int x = x + 1;
compile sans avertissement dans Visual C #. Peut-être la même chose en Java?x est non initialisé dans
x = x + 1
;.Le langage de programmation Java est de type statique, ce qui signifie que toutes les variables doivent d'abord être déclarées avant de pouvoir être utilisées.
Voir les types de données primitifs
la source
La ligne de code ne se compile pas avec un avertissement en raison de la façon dont le code fonctionne réellement. Lorsque vous exécutez le code
int x = x = 1
, Java crée d'abord la variablex
, comme défini. Ensuite, il exécute le code d'affectation (x = 1
). Commex
est déjà défini, le système n'a pas d'erreurs en définissantx
sur 1. Cela renvoie la valeur 1, car c'est maintenant la valeur dex
. Par conséquent,x
est maintenant finalement défini sur 1.Java exécute le code comme s'il s'agissait de ceci:
Cependant, dans votre deuxième morceau de code ,,
int x = x + 1
l'+ 1
instruction doitx
être définie, ce qui n'est plus le cas. Étant donné que les instructions d'affectation signifient toujours que le code à droite de=
est exécuté en premier, le code échouera car ilx
n'est pas défini. Java exécuterait le code comme ceci:la source
Complier a lu les déclarations de droite à gauche et nous avons conçu pour faire le contraire. C'est pourquoi cela a agacé au début. Faites-en une habitude pour lire les instructions (code) de droite à gauche, vous n'aurez pas un tel problème.
la source