Je comprends que, en Java, l’affectation correspond à la valeur de l’opérande de droite. Par conséquent, des instructions comme x == (y = x)
sont évaluées à true
.
Ce code, cependant, affiche false
.
public static void main(String[]args){
String x = "hello";
String y = "goodbye";
System.out.println(x.equals(x = y));
}
Pourquoi est-ce? À ma connaissance, il évalue d’abord (x = y)
, Qui attribue à x
la valeur de y
, puis renvoie la valeur de y
. Ensuite, x.equals(y)
est évalué, ce qui devrait être true
puisque x
et y
devraient maintenant partager les mêmes références, mais je reçois plutôt false
.
Que se passe-t-il ici?
Tout d’abord: c’est une question intéressante, mais ne devrait jamais apparaître dans un "code réel", car assigner à la variable que vous appelez sur la même ligne est source de confusion, même si vous savez comment cela fonctionne.
Qu'est-ce qui se passe ici est ces 3 étapes:
x
, cela entraînera une référence à la chaîne "hello")x = y
, ce qui modifiera x
pour qu'il pointe vers la chaîne "au revoir" et renvoie également une référence à cette chaîne)equals
sur le résultat de # 1 en utilisant le résultat de # 2 comme paramètre (qui seront respectivement les références aux chaînes "hello" et "au revoir").Regarder le code d'octet produit pour cette méthode montre clairement (en supposant que vous maîtrisez le bytecode Java):
0: ldc #2 // String hello
2: astore_1
3: ldc #3 // String goodbye
5: astore_2
6: getstatic #4 // Field Java/lang/System.out:Ljava/io/PrintStream;
9: aload_1
10: aload_2
11: dup
12: astore_1
13: invokevirtual #5 // Method Java/lang/String.equals:(Ljava/lang/Object;)Z
16: invokevirtual #6 // Method Java/io/PrintStream.println:(Z)V
19: return
La ligne 9 est l’étape 1 ci-dessus (c’est-à-dire évalue x
et se souvient de la valeur).
La ligne 10-12 est l’étape 2. Elle charge y
, la duplique (une fois pour l’affectation, une fois pour la valeur de retour de l’expression d’affectation) et l’assigne à x
.
La ligne 13 appelle equals
sur le résultat calculé dans la ligne 9 et le résultat des lignes 10 à 12.
Bonne question! Et le JLS a la réponse ...
§15.12.4.1 (Exemple 15.12.4.1-2). Ordre d'évaluation pendant l'invocation de méthode:
Dans le cadre d'une invocation de méthode d'instance, il existe une expression qui désigne l'objet à appeler. Cette expression semble être entièrement évaluée avant l'évaluation d'une partie d'une expression d'argument associée à l'appel de la méthode.
Donc, dans:
String x = "hello";
String y = "goodbye";
System.out.println(x.equals(x = y));
l'occurrence de x
avant .equals
est évalué en premier, avant l'expression de l'argument x = y
.
Par conséquent, une référence à la chaîne hello
est mémorisée comme référence à la cible avant que la variable locale x
ne soit modifiée pour faire référence à la chaîne goodbye
. En conséquence, la méthode equals
est invoquée pour l'objet cible hello
avec l'argument goodbye
. Le résultat de l'appel est donc false
.
Il est important de se rappeler qu'un String
in Java est un objet, et donc une référence. Lorsque vous appelez
x.equals(...)
Il vérifie si la valeur à l'emplacement actuellement référencé par x
est égale à ce que vous transmettez. À l'intérieur, vous modifiez la valeur que x
est référencement , mais vous appelez toujours equals
avec la référence originale (la référence à "hello"). Donc, en ce moment, votre code compare pour voir si "bonjour" est égal à "au revoir", ce qu'il n'est clairement pas. Après cela, si vous utilisez à nouveau x
, il en résultera une référence à la même valeur que y.
x=y
entre parenthèses signifie que l'expression (x=y)
est maintenant goodbye
, alors que le x externe dans x.equals
détient la valeur hello
Reimus a donné la bonne réponse, mais j'aimerais élaborer.
Dans Java (et la plupart des langues), la convention est la variable va à gauche, affectation à droite.
Décomposons:
String x = "hello";
//x <- "hello"
String y = "goodbye";
//y <- "goodbye";
Pour des raisons de débogage et de lisibilité du code, il est toujours judicieux de diviser vos lignes afin qu’elles ne fassent qu’une chose.
System.out.println(x.equals(x = y)); //Compound statement
Ici, x.equals(...)
est appelé sur la référence d'origine à x, ou "bonjour", il est mis à jour pour la deuxième référence.
Je voudrais écrire ceci comme (et cela vous donnera la réponse attendue):
x = y;
// x <- y = "goodbye"
boolean xEqualsX = x.equals(x);
// xEqualsX <- true
System.out.println(xEqualsX);
// "true"
Cela semble évident qu'il devrait en être ainsi, mais il est également très facile de voir exactement ce qui se passe dans chaque ligne, ce que vous devez rechercher.
J'ai essayé votre question dans Eclipse vos deux expressions sont correctes. 1) x == (y = x) est évalué comme vrai il est vrai parce que la valeur de x est assignée à y qui est 'bonjour' alors x et y comparent, ils seront identiques, donc le résultat sera vrai
2) x.equal (x = y) il est faux parce que la valeur de y est assignée à x ce qui est un au revoir, alors x et x comparent leur valeur sera différente donc le résultat sera faux
Je vois la question en termes profanes comme "hello".equals("goodbye")
. Donc, ça retourne faux.
In Java String est une classe.
String x = "hello";
String y = "goodbye";
est une chaîne différente qui fait référence à deux valeurs différentes qui ne sont pas identiques et si vous comparez
System.out.println(x.equals(x = y));
//this compare value (hello and goodbye) return true
System.out.println(x == (y = x));
// this compare reference of an object (x and y) return false