Comment la classe String remplace-t-elle l'opérateur +?

129

Pourquoi en Java vous pouvez ajouter des chaînes avec l'opérateur +, alors que String est une classe? Dans le String.javacode, je n'ai trouvé aucune implémentation pour cet opérateur. Ce concept viole-t-il l'orientation de l'objet?

Pooya
la source
21
L' +opérateur Plus ( ) est une fonctionnalité de langage de Java.
adatapost
18
C'est toute la magie du compilateur. Vous ne pouvez pas effectuer de surcharge d'opérateurs en Java.
Lion
18
Je pense que la chose étrange est que String est implémenté en tant que bibliothèque en dehors du langage (dans java.lang.String), mais il a un support spécifique à l' intérieur du langage. Ce n'est pas juste.
13ren du
@ 13ren vous vous trompez. String fait partie de la classe java.lang qui signifie language - le langage java !!! Toutes les classes de ce package sont spéciales. Notez que vous n'avez pas besoin d'importer java.lang pour les utiliser.

Réponses:

156

Regardons les expressions simples suivantes en Java

int x=15;
String temp="x = "+x;

Le compilateur se convertit "x = "+x;en un en StringBuilderinterne et utilise .append(int)pour "ajouter" l'entier à la chaîne.

5.1.11. Conversion de chaîne

Tout type peut être converti en type String par conversion de chaîne.

Une valeur x de type primitif T est d'abord convertie en valeur de référence comme si en la donnant comme argument à une expression de création d'instance de classe appropriée (§15.9):

  • Si T est booléen, alors utilisez new Boolean (x).
  • Si T est un caractère, utilisez le nouveau caractère (x).
  • Si T est byte, short ou int, utilisez le nouvel Integer (x).
  • Si T est long, utilisez le nouveau Long (x).
  • Si T est float, alors utilisez new Float (x).
  • Si T est double, utilisez le nouveau Double (x).

Cette valeur de référence est ensuite convertie en type String par conversion de chaîne.

Désormais, seules les valeurs de référence doivent être prises en compte:

  • Si la référence est nulle, elle est convertie en la chaîne "null" (quatre caractères ASCII n, u, l, l).
  • Sinon, la conversion est effectuée comme si par un appel de la méthode toString de l'objet référencé sans argument; mais si le résultat de l'appel de la méthode toString est nul, alors la chaîne "null" est utilisée à la place.

La méthode toString est définie par la classe primordiale Object (§4.3.2). De nombreuses classes le remplacent, notamment Boolean, Character, Integer, Long, Float, Double et String.

Voir §5.4 pour plus de détails sur le contexte de conversion de chaîne.

15.18.1.

Optimisation de la concaténation de chaînes: une implémentation peut choisir d'effectuer la conversion et la concaténation en une seule étape pour éviter de créer puis de supprimer un objet String intermédiaire. Pour augmenter les performances de la concaténation de chaînes répétées, un compilateur Java peut utiliser la classe StringBuffer ou une technique similaire pour réduire le nombre d'objets String intermédiaires créés par l'évaluation d'une expression.

Pour les types primitifs, une implémentation peut également optimiser la création d'un objet wrapper en convertissant directement un type primitif en chaîne.

La version optimisée ne fera pas d'abord une conversion de chaîne complète.

Ceci est une bonne illustration d'une version optimisée utilisée par le compilateur, bien que sans la conversion d'une primitive, où vous pouvez voir le compilateur changer les choses en StringBuilder en arrière-plan:

http://caprazzi.net/posts/java-bytecode-string-concatenation-and-stringbuilder/


Ce code java:

public static void main(String[] args) {
    String cip = "cip";
    String ciop = "ciop";
    String plus = cip + ciop;
    String build = new StringBuilder(cip).append(ciop).toString();
}

Génère ceci - voyez comment les deux styles de concaténation mènent au même bytecode:

 L0
    LINENUMBER 23 L0
    LDC "cip"
    ASTORE 1
   L1
    LINENUMBER 24 L1
    LDC "ciop"
    ASTORE 2

   // cip + ciop

   L2
    LINENUMBER 25 L2

    NEW java/lang/StringBuilder
    DUP
    ALOAD 1
    INVOKESTATIC java/lang/String.valueOf(Ljava/lang/Object;)Ljava/lang/String;
    INVOKESPECIAL java/lang/StringBuilder.<init>(Ljava/lang/String;)V
    ALOAD 2
    INVOKEVIRTUAL java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lang/StringBuilder;
    INVOKEVIRTUAL java/lang/StringBuilder.toString()Ljava/lang/String;

    ASTORE 3

    // new StringBuilder(cip).append(ciop).toString()

   L3
    LINENUMBER 26 L3

    NEW java/lang/StringBuilder
    DUP
    ALOAD 1
    INVOKESPECIAL java/lang/StringBuilder.<init>(Ljava/lang/String;)V
    ALOAD 2
    INVOKEVIRTUAL java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lang/StringBuilder;
    INVOKEVIRTUAL java/lang/StringBuilder.toString()Ljava/lang/String;

    ASTORE 4
   L4
    LINENUMBER 27 L4
    RETURN

En regardant l'exemple ci-dessus et comment le code d'octet basé sur le code source dans l'exemple donné est généré, vous pourrez remarquer que le compilateur a transformé en interne l'instruction suivante

cip+ciop; 

dans

new StringBuilder(cip).append(ciop).toString();

En d'autres termes, l'opérateur +dans la concaténation de chaînes est en fait un raccourci pour l' StringBuilderidiome plus verbeux .

Lion
la source
3
Merci beaucoup, je ne suis pas familier avec les codes d'octets jvm mais le code généré pour String plus = cip + ciop; et String build = new StringBuilder (cip) .append (ciop) .toString (); sont identiques. et ma question est que cette opération viole l'orientation de l'objet?
Pooya
7
Non, ce n'est pas le cas. La surcharge des opérateurs (comme en C ++ et dans certains langages) a quelques inconvénients et les concepteurs Java ont estimé que c'était un concept quelque peu déroutant et l'ont omis de Java. Pour moi, un langage orienté objet doit avoir les principaux concepts d'héritage, de polymorphisme et d'encapsulation que Java possède.
Lion
2
oui, mais je pense que cet opérateur a surchargé pour la classe String
Pooya
3
Oui, la surcharge d'opérateurs est utilisée en Java pour la concaténation du type String, cependant, vous ne pouvez pas définir votre propre opérateur (comme en C ++, C # et certains autres langages).
Lion
5
@Pooya: en fait, "int / int" contre "int / float" est déjà une surcharge d'opérateur, donc même C en a. Ce que C (et Java) n'ont cependant pas, c'est la surcharge d'opérateurs définis par l'utilisateur: la seule chose qui définit les différentes façons dont un opérateur peut être utilisé (en C et Java) est la définition du langage (et la distinction est implémentée dans le compilateur). C ++ diffère en ce qu'il permet une surcharge d'opérateur définie par l'utilisateur (souvent appelée simplement "surcharge d'opérateur").
Joachim Sauer
27

C'est la fonction du compilateur Java qui vérifie les opérandes de l' +opérateur. Et basé sur les opérandes, il génère le code d'octet:

  • Pour String, il génère du code pour concaténer des chaînes
  • Pour les nombres, il génère du code pour ajouter des nombres.

Voici ce que dit la spécification Java :

Les opérateurs + et -sont appelés les opérateurs additifs. AdditiveExpression: MultiplicativeExpression AdditiveExpression + MultiplicativeExpression AdditiveExpression - MultiplicativeExpression

Les opérateurs additifs ont la même priorité et sont syntaxiquement associatifs à gauche (ils regroupent de gauche à droite). Si le type de l'un ou l'autre des opérandes d'un +opérateur est String, l'opération est une concaténation de chaînes.

Sinon, le type de chacun des opérandes de l' +opérateur doit être un type convertible (§5.1.8) en un type numérique primitif, sinon une erreur de compilation se produit.

Dans tous les cas, le type de chacun des opérandes de l' -opérateur binaire doit être un type convertible (§5.1.8) en un type numérique primitif, sinon une erreur de compilation se produit.

Ramesh PVK
la source
7
La citation de la spécification n'est pas du tout pertinente pour cette question.
Ernest Friedman-Hill
Ceci est l'extrait "Si le type de l'un des opérandes d'un opérateur + est String, alors l'opération est une concaténation de chaînes. Sinon, le type de chacun des opérandes de l'opérateur + doit être un type convertible (§5.1.8 ) à un type numérique primitif, ou une erreur de compilation se produit ". Pouvez-vous me dire pourquoi ce n'est pas pertinent.
Ramesh PVK
7
Il ne dit rien sur la façon dont il est mis en œuvre, c'est la question. Je pense que l'affiche comprend déjà que la fonctionnalité existe.
Ernest Friedman-Hill
14

Comment la classe String remplace l'opérateur +?

Ce n'est pas le cas. Le compilateur le fait. Strictement parlant, le compilateur surcharge l'opérateur + pour les opérandes String.

Marquis de Lorne
la source
6

Tout d'abord (+) est surchargé et non remplacé

Le langage Java fournit un support spécial pour l'opérateur de concaténation de chaînes (+), qui a été surchargé pour les objets Java Strings.

  1. Si l'opérande de gauche est String, il fonctionne comme une concaténation.

  2. Si l'opérande de gauche est Integer, il fonctionne comme opérateur d'addition

Pramod Kumar
la source
3
(2) Si l'opérande de gauche est un entier, il est automatiquement déballé intet les règles normales de Java s'appliquent.
Marquis of Lorne
2
Les deux règles données sous la citation sont fausses: je crois qu'elles devraient être: deux primitives (ou classes non boxables) = addition; au moins une chaîne = concaténation
mwfearnley
4

Le langage Java fournit un support spécial pour l'opérateur de concaténation de chaînes (+) et pour la conversion d'autres objets en chaînes. La concaténation de chaînes est implémentée via la classe StringBuilder(ou StringBuffer) et sa appendméthode.

Jigar Pandya
la source
4

La signification de l' +opérateur lorsqu'il est appliqué à Stringest définie par le langage, comme tout le monde l'a déjà écrit. Puisque vous ne semblez pas trouver cela suffisamment convaincant, considérez ceci:

Les entiers, les flottants et les doubles ont tous des représentations binaires différentes, et donc ajouter deux entiers est une opération différente, en termes de manipulation de bits, que d'ajouter deux flottants: pour les entiers, vous pouvez ajouter petit à petit, en portant un bit et en vérifiant le débordement; pour les flotteurs, vous devez traiter les mantisses et les exposants séparément.

Ainsi, en principe, «l'addition» dépend de la nature des objets «ajoutés». Java le définit pour les chaînes ainsi que pour les entiers et les flottants (longs, doubles, ...)

Alexis
la source
3

L' +opérateur est généralement remplacé par un StringBuilderau moment de la compilation. Vérifiez cette réponse pour plus de détails à ce sujet.

Scorpion
la source
Si tel est le cas, y a-t-il une raison pour laquelle StringBuilder existe pour un usage public? Y a-t-il des cas où l' +opérateur n'est pas remplacé par StringBuilder?
Cemre
2
La question que vous voulez vous poser est "pourquoi l'opérateur + existe-t-il pour un usage public?", Car c'est l'abomination ici. Quant à votre autre question, je ne la connais pas exactement, mais j'imagine qu'il n'y en a pas.
Scorpion
Le compilateur peut utiliser concat () à la place, s'il n'y a que deux éléments. De plus, le compilateur ne parvient pas à remplacer concat () par StringBuilder (ou utilise plusieurs StringBuilders et les ajoute ensemble) lorsque le programmeur construit une chaîne dans un long code imbriqué / en boucle - l'utilisation de StringBuilder unique et explicite sera meilleure pour les performances.
user158037