web-dev-qa-db-fra.com

Java exécution de l'opérateur d'affectation

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.

Screenshot showing the source and that the output is "false"

Que se passe-t-il ici?

74
Sam

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:

  1. déterminer sur quel objet appeler la méthode (c'est-à-dire évaluer le premier x, cela entraînera une référence à la chaîne "hello")
  2. déterminer les paramètres (c'est-à-dire évaluer 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)
  3. appelez la méthode 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.

74
Joachim Sauer

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.

37
Oleksandr Pyrohov

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.

27
Keveloper

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

5
Chetan Jadhav CD

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.

4
Randomness Slayer

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

2
Alishan

Je vois la question en termes profanes comme "hello".equals("goodbye"). Donc, ça retourne faux.

1
Vamsi

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  
1
Aadesk