¿Cómo anula la clase String el operador +?

129

¿Por qué en Java puedes agregar Strings con el operador +, cuando String es una clase? En el String.javacódigo no encontré ninguna implementación para este operador. ¿Este concepto viola la orientación a objetos?

Pooya
fuente
21
El +operador Plus ( ) es una característica del lenguaje de Java.
adatapost
18
Todo es magia del compilador. No se puede hacer la sobrecarga del operador en Java.
León
18
Creo que lo extraño es que String se implementa como una biblioteca fuera del lenguaje (en java.lang.String), pero tiene soporte específico dentro del lenguaje. Eso no está bien.
13ren
@ 13ren estás equivocado. La cadena es aparte de la clase java.lang que significa lenguaje: ¡el lenguaje java! Todas las clases en este paquete son especiales. Tenga en cuenta que no necesita importar java.lang para usarlos.

Respuestas:

156

Veamos las siguientes expresiones simples en Java

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

Los conversos del compilador "x = "+x;en una StringBuilderinterna y usos .append(int)de "añadir" el entero a la cadena.

5.1.11. Conversión de cadenas

Cualquier tipo se puede convertir a tipo Cadena por conversión de cadena.

Un valor x de tipo primitivo T se convierte primero en un valor de referencia como si lo diera como argumento a una expresión de creación de instancia de clase apropiada (§15.9):

  • Si T es booleano, utilice el nuevo booleano (x).
  • Si T es char, entonces use el nuevo Carácter (x).
  • Si T es byte, short o int, use un nuevo entero (x).
  • Si T es largo, utilice el nuevo Largo (x).
  • Si T es flotante, entonces use el nuevo Flotador (x).
  • Si T es doble, entonces use el nuevo Doble (x).

Este valor de referencia luego se convierte al tipo Cadena por conversión de cadena.

Ahora solo se deben considerar los valores de referencia:

  • Si la referencia es nula, se convierte a la cadena "nulo" (cuatro caracteres ASCII n, u, l, l).
  • De lo contrario, la conversión se realiza como si fuera una invocación del método toString del objeto referenciado sin argumentos; pero si el resultado de invocar el método toString es nulo, entonces se usa la cadena "nulo".

El método toString está definido por la clase primordial Object (§4.3.2). Muchas clases lo anulan, especialmente Boolean, Character, Integer, Long, Float, Double y String.

Ver §5.4 para detalles del contexto de conversión de cadenas.

15.18.1.

Optimización de la concatenación de cadenas: una implementación puede optar por realizar la conversión y la concatenación en un solo paso para evitar crear y luego descartar un objeto de cadena intermedio. Para aumentar el rendimiento de la concatenación de cadenas repetida, un compilador de Java puede usar la clase StringBuffer o una técnica similar para reducir el número de objetos de cadena intermedios que se crean mediante la evaluación de una expresión.

Para los tipos primitivos, una implementación también puede optimizar la creación de un objeto contenedor mediante la conversión directa de un tipo primitivo a una cadena.

La versión optimizada no realizará primero una conversión de String completa.

Esta es una buena ilustración de una versión optimizada utilizada por el compilador, aunque sin la conversión de una primitiva, donde puede ver el compilador cambiando las cosas en un StringBuilder en segundo plano:

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


Este código de 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();
}

Genera esto: vea cómo los dos estilos de concatenación conducen al mismo código de bytes:

 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

Si observa el ejemplo anterior y cómo se genera el código de byte basado en el código fuente en el ejemplo dado, podrá observar que el compilador ha transformado internamente la siguiente instrucción

cip+ciop; 

dentro

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

En otras palabras, el operador +en la concatenación de cadenas es efectivamente una abreviatura para el StringBuilderlenguaje más detallado .

León
fuente
3
Muchas gracias, no estoy familiarizado con los códigos de byte jvm, pero generé código para String plus = cip + ciop; y String build = new StringBuilder (cip) .append (ciop) .toString (); son idénticos. y mi pregunta es si esta operación viola la orientación a objetos.
Pooya
77
No lo hace La sobrecarga del operador (como en C ++ y algunos lenguajes) tiene algunos inconvenientes y los diseñadores de Java consideraron que es un concepto algo confuso y lo omitieron de Java. Para mí, un lenguaje orientado a objetos debe tener los conceptos principales de herencia, polimorfismo y encapsulación que tiene Java.
Lion
2
sí, pero creo que este operador se ha sobrecargado para la clase String
Pooya
3
Sí, la sobrecarga del operador se usa en Java para la concatenación del tipo de cadena; sin embargo, no puede definir su propio operador (como en C ++, C # y algunos otros lenguajes).
Lion
55
@Pooya: en realidad "int / int" vs. "int / float" ya está sobrecargado por el operador, por lo que incluso C tiene eso. Sin embargo, lo que C (y Java) no tienen es la sobrecarga del operador definida por el usuario: lo único que define las diferentes formas en que se puede usar un operador (tanto en C como en Java) es la definición del lenguaje (y la distinción se implementa en el compilador). C ++ difiere en que permite la sobrecarga del operador definida por el usuario (que a menudo se denomina simplemente "sobrecarga del operador").
Joachim Sauer
27

Es la función del compilador de Java que verifica los operandos del +operador. Y en función de los operandos, genera el código de bytes:

  • Para String, genera código para concatenar cadenas
  • Para Números, genera código para agregar números.

Esto es lo que dice la especificación de Java :

Los operadores + y -se denominan operadores aditivos. AdditiveExpression: MultiplicativeExpression AdditiveExpression + MultiplicativeExpression AdditiveExpression - MultiplicativeExpression

Los operadores aditivos tienen la misma precedencia y son sintácticamente asociativos a la izquierda (se agrupan de izquierda a derecha). Si el tipo de cualquiera de los operandos de un +operador es String, entonces la operación es la concatenación de cadenas.

De lo contrario, el tipo de cada uno de los operandos del +operador debe ser un tipo que sea convertible (§5.1.8) a un tipo numérico primitivo, o se producirá un error en tiempo de compilación.

En todos los casos, el tipo de cada uno de los operandos del -operador binario debe ser un tipo que sea convertible (§5.1.8) a un tipo numérico primitivo, o se producirá un error en tiempo de compilación.

Ramesh PVK
fuente
77
La cita de la especificación no es relevante para esta pregunta en absoluto.
Ernest Friedman-Hill
Este es el extracto "Si el tipo de cualquier operando de un operador + es String, entonces la operación es la concatenación de cadenas. De lo contrario, el tipo de cada uno de los operandos del operador + debe ser un tipo convertible (§5.1.8 ) a un tipo numérico primitivo, o se produce un error en tiempo de compilación ". ¿Me puede decir por qué no es relevante?
Ramesh PVK
77
No dice nada sobre cómo se implementa, que es la pregunta. Creo que el póster ya entiende que la característica existe.
Ernest Friedman-Hill
14

¿Cómo anula la clase String + operador?

No lo hace. El compilador lo hace. Estrictamente hablando, el compilador sobrecarga el operador + para los operandos String.

Marqués de Lorne
fuente
6

En primer lugar (+) está sobrecargado, no anulado

El lenguaje Java proporciona soporte especial para el operador de concatenación de cadenas (+), que se ha sobrecargado para los objetos de cadenas Java.

  1. Si el operando del lado izquierdo es String, funciona como concatenación.

  2. Si el operando del lado izquierdo es Entero, funciona como operador de suma

Pramod Kumar
fuente
3
(2) Si el operando izquierdo es un entero, se desempaqueta automáticamente inty luego se aplican las reglas normales de Java.
Marqués de Lorne
2
Las dos reglas dadas debajo de la cita son incorrectas: creo que deberían ser: dos primitivas (o clases que no se pueden encajonar) = suma; al menos una cadena = concatenación
mwfearnley
4

El lenguaje Java proporciona soporte especial para el operador de concatenación de cadenas (+) y para la conversión de otros objetos a cadenas. La concatenación de cadenas se implementa a través de la clase StringBuilder(o StringBuffer) y su appendmétodo.

Jigar Pandya
fuente
4

El significado del +operador cuando se aplica Stringestá definido por el idioma, como todos ya han escrito. Como no parece encontrar esto suficientemente convincente, considere esto:

Los entrantes, flotantes y dobles tienen representaciones binarias diferentes y, por lo tanto, agregar dos entradas es una operación diferente, en términos de manipulación de bits, que agregar dos flotantes: para las entradas puede agregar bit por bit, transportar un bit y verificar el desbordamiento; para flotadores debes tratar con las mantisas y exponentes por separado.

Entonces, en principio, la "adición" depende de la naturaleza de los objetos que se "agregan". Java lo define para cadenas, así como entradas y flotantes (largos, dobles, ...)

alexis
fuente
3

El +operador generalmente se reemplaza por un StringBuilderen tiempo de compilación. Verifique esta respuesta para obtener más detalles al respecto.

Escorpión
fuente
Si este es el caso, ¿hay alguna razón por la cual StringBuilder existe para uso público? ¿Hay algún caso en el que el +operador no sea reemplazado por StringBuilder?
Cemre
2
La pregunta que desea hacer es "¿por qué el operador + existe para uso público?", Porque esa es la abominación aquí. En cuanto a su otra pregunta, no lo sé exactamente, pero supongo que no existe tal caso.
Escorpio
El compilador podría usar concat () en su lugar, si solo hay dos elementos. Además, el compilador no puede reemplazar concat () con StringBuilder (o usa varios StringBuilders y los agrega juntos) cuando el programador está creando una cadena en código anidado / en bucle largo; usar StringBuilder explícito único será mejor para el rendimiento.
user158037