Java Language Piège: utiliser == pour comparer des chaînes


Exemple

Une erreur courante pour les débutants Java est d'utiliser l'opérateur == pour tester si deux chaînes sont égales. Par exemple:

public class Hello {
    public static void main(String[] args) {
        if (args.length > 0) {
            if (args[0] == "hello") {
                System.out.println("Hello back to you");
            } else {
                System.out.println("Are you feeling grumpy today?");
            }
        }
    }
}

Le programme ci-dessus est censé tester le premier argument de la ligne de commande et imprimer différents messages lorsqu'il ne s'agit pas du mot "bonjour". Mais le problème est que cela ne fonctionnera pas. Ce programme produira "Êtes-vous grincheux aujourd'hui?" quel que soit le premier argument de la ligne de commande.

Dans ce cas particulier, la String "hello" est placée dans le pool de chaînes pendant que les arguments String [0] résident sur le tas. Cela signifie qu'il y a deux objets représentant le même littéral, chacun avec sa référence. Étant donné que == teste les références et non l’égalité réelle, la comparaison produira un faux la plupart du temps. Cela ne signifie pas qu'il le fera toujours.

Lorsque vous utilisez == pour tester des chaînes, ce que vous testez en réalité est si deux objets String sont le même objet Java. Malheureusement, cela ne signifie pas l’égalité des chaînes en Java. En fait, la méthode correcte pour tester les chaînes consiste à utiliser la méthode equals(Object) . Pour une paire de chaînes, nous voulons généralement tester si elles sont composées des mêmes caractères dans le même ordre.

public class Hello2 {
    public static void main(String[] args) {
        if (args.length > 0) {
            if (args[0].equals("hello")) {
                System.out.println("Hello back to you");
            } else {
                System.out.println("Are you feeling grumpy today?");
            }
        }
    }
}

Mais en réalité, ça empire. Le problème est que == donnera la réponse attendue dans certaines circonstances. Par exemple

public class Test1 {
    public static void main(String[] args) {
        String s1 = "hello";
        String s2 = "hello";
        if (s1 == s2) {
            System.out.println("same");
        } else {
            System.out.println("different");
        }
    }
}

Il est intéressant de noter que cela affichera "identique", même si nous testons les chaînes dans le mauvais sens. Pourquoi donc? Parce que la spécification de langage Java (Section 3.10.5: Littéraux de chaîne) stipule que deux chaînes >> littérales << composées des mêmes caractères seront effectivement représentées par le même objet Java. Par conséquent, le test == sera vrai pour les littéraux égaux. (Les littéraux de chaîne sont "internés" et ajoutés à un "pool de chaînes" partagé lorsque votre code est chargé, mais il s'agit en fait d'un détail d'implémentation.)

Pour ajouter à la confusion, la spécification de langage Java stipule également que lorsque vous avez une expression constante de compilation qui concatène deux littéraux de chaîne, cela équivaut à un seul littéral. Ainsi:

    public class Test1 {
    public static void main(String[] args) {
        String s1 = "hello";
        String s2 = "hel" + "lo";
        String s3 = " mum";
        if (s1 == s2) {
            System.out.println("1. same");
        } else {
            System.out.println("1. different");
        }
        if (s1 + s3 == "hello mum") {
            System.out.println("2. same");
        } else {
            System.out.println("2. different");
        }
    }
}

Cela produira "1. mêmes" et "2. différents". Dans le premier cas, l'expression + est évaluée au moment de la compilation et nous comparons un objet String avec lui-même. Dans le second cas, il est évalué à l'exécution et nous comparons deux objets String différents

En résumé, l'utilisation de == pour tester des chaînes en Java est presque toujours incorrecte, mais il n'est pas certain que la réponse soit incorrecte.