75 votes

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

En Java, je comprends que l'affectation est évaluée à la valeur de l'opérande de droite, de sorte que des déclarations comme x == (y = x) évaluer à l' true.

Ce code, cependant, sorties false.

public static void main(String[]args){
    String x = "hello";
    String y = "goodbye";
    System.out.println(x.equals(x = y));
}

Pourquoi est-ce? Dans ma compréhension, il évalue d'abord (x = y), ce qui affecte x de la valeur de y, puis renvoie la valeur de y. Ensuite, x.equals(y) est évaluée, ce qui devrait être true depuis x et y devraient partager les mêmes références, maintenant, mais au lieu de cela, je reçois false.

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

Ce qui se passe ici?

75voto

Joachim Sauer Points 133411

Tout d'abord: c'est une question intéressante, mais ne doit jamais venir dans "le code réel", comme l'affectation à la variable de vous appeler dans la même ligne est déroutant, même si vous savez comment cela fonctionne.

Ce qui se passe ici est de ces 3 étapes:

  1. déterminer quel objet pour appeler la méthode sur (c'est à dire l'évaluation de la première x, cela se traduira par une référence à la Chaîne "bonjour")
  2. comprendre les paramètres (c'est à dire évaluer x = y, ce qui changera x de point à la Chaîne "au revoir" et également renvoyer une référence à la Chaîne)
  3. l'appel à la méthode equals sur le résultat de #1 à l'aide de la suite n ° 2 en tant que paramètre (qui seront des références pour les Chaînes de caractères "bonjour" et "au revoir", respectivement).

En regardant le byte code produit pour cette méthode, il est clair (en supposant que vous êtes à l'aise dans 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

Ligne n ° 9 est l'étape 1 ci-dessus (c'est à dire évalue x et mémorise la valeur).

Ligne n ° 10-12 est l'étape 2. Il charge y, les doublons, il (une fois pour l'attribution, une fois pour la valeur de retour de la mission de l'expression) et l'attribue à l' x.

Ligne n ° 13 invoque equals sur le résultat calculé dans la Ligne #9 et le résultat de Lignes #10-12.

37voto

Oleksandr Points 7545

Bonne question! Et le JLS a la réponse...

§15.12.4.1 (Exemple 15.12.4.1-2). L'Évaluation De L'Ordre Lors De L'Invocation De Méthode:

Dans le cadre d'une instance d'appel de méthode, il y a une expression qui indique que l'objet puisse être invoquée. Cette expression semble être entièrement évalué avant toute partie d'une expression d'argument à la méthode l'invocation est évalué.

Ainsi, dans:

String x = "hello";
String y = "goodbye";
System.out.println(x.equals(x = y));

la survenue d' x avant .equals est évalué en premier, avant l'argument de l'expression x = y.

Par conséquent, une référence à la chaîne de caractères hello on se souvient que la cible de référence avant la variable locale x est modifiée pour faire référence à la chaîne de caractères goodbye. En conséquence, l' equals méthode est invoquée pour objet cible hello avec l'argument goodbye, de sorte que le résultat de l'invocation est - false.

27voto

Keveloper Points 733

Il est important de se rappeler qu'un String en java est un objet, et par conséquent, une référence. Lorsque vous appelez

x.equals(...)

Il est en train de vérifier si la valeur à l'emplacement actuellement référencé par x est égal à ce que vous êtes de passage dans. À l'intérieur, vous modifiez la valeur qu' x est le référencement, mais vous êtes encore en les appelant equals avec l' original de référence (la référence à "bonjour"). Alors, maintenant, votre code est de comparer pour voir si "bonjour" est égal à "au revoir", elle est clairement non. Après ce point, si vous utilisez x de nouveau, il en résultera une référence à la même valeur de y.

5voto

Chetan Jadhav CD Points 688

x=y entre parenthèses signifie que l'expression (x=y) est désormais goodbye , tandis que le x extérieur en x.equals contient la valeur hello

4voto

Randomness Slayer Points 394

Reimus donné la bonne réponse, mais j'aimerais décrire.

En Java (et de la plupart des langues) de la convention est variable va sur la gauche, la cession sur la droite.

Nous allons le décomposer:

String x = "hello";
//x <- "hello"

String y = "goodbye";
//y <- "goodbye";

À des fins de débogage ainsi que la lisibilité du code, il est toujours une bonne pratique de diviser vos lignes de manière à ce qu'ils ne font qu'une seule chose.

System.out.println(x.equals(x = y)); //Compound statement

Ici, x.equals(...) est appelée sur la référence d'origine de x, ou "bonjour", il est mis à jour pour la seconde référence.

Je voudrais écrire ce que (et cela va vous donner votre réponse attendue):

x = y;
// x <- y = "goodbye"

boolean xEqualsX = x.equals(x);
// xEqualsX <- true

System.out.println(xEqualsX);
// "true"

Maintenant, cela semble évident qu'il doit se comporter de cette façon, mais il est également très facile de voir exactement ce qui se passe dans chaque ligne, ce qui est quelque chose que vous devriez vous efforcer d'.

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: