Como a classe String substitui o operador +?

129

Por que em Java você pode adicionar Strings com o operador +, quando String é uma classe? No String.javacódigo, não encontrei nenhuma implementação para este operador. Esse conceito viola a orientação do objeto?

Pooya
fonte
21
O +operador Plus ( ) é um recurso de linguagem do Java.
Adatapost
18
É tudo mágico do compilador. Você não pode sobrecarregar o operador em Java.
Lion
18
Eu acho que o mais estranho é que o String é implementado como uma biblioteca fora da linguagem (em java.lang.String), mas possui suporte específico dentro da linguagem. Isso não está certo.
13ren
@ 13ren você está errado. String é parte da classe java.lang, que significa idioma - o idioma do java !!! Todas as classes neste pacote são especiais. Observe que você não precisa importar o java.lang para usá-los.

Respostas:

156

Vejamos as seguintes expressões simples em Java

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

O compilador converte "x = "+x;em um StringBuilderinternamente e usa .append(int)para "adicionar" o número inteiro à seqüência de caracteres.

5.1.11 Conversão de String

Qualquer tipo pode ser convertido no tipo String por conversão de string.

Um valor x do tipo primitivo T é primeiro convertido em um valor de referência como se fosse fornecido como argumento para uma expressão de criação de instância de classe apropriada (§15.9):

  • Se T for booleano, use o novo booleano (x).
  • Se T for char, use o novo caractere (x).
  • Se T for byte, curto ou int, use o novo número inteiro (x).
  • Se T for longo, use o novo Longo (x).
  • Se T for flutuante, use o novo flutuador (x).
  • Se T for o dobro, use o novo Duplo (x).

Esse valor de referência é então convertido para o tipo String por conversão de string.

Agora, apenas os valores de referência precisam ser considerados:

  • Se a referência for nula, ela será convertida na cadeia "nula" (quatro caracteres ASCII n, u, l, l).
  • Caso contrário, a conversão é executada como se por uma invocação do método toString do objeto referenciado sem argumentos; mas se o resultado da chamada do método toString for nulo, a sequência "null" será usada.

O método toString é definido pela classe primordial Object (§4.3.2). Muitas classes o substituem, especialmente Boolean, Character, Inteiro, Long, Float, Double e String.

Veja §5.4 para detalhes do contexto de conversão de strings.

15.18.1

Otimização da concatenação de strings: uma implementação pode optar por executar a conversão e concatenação em uma etapa para evitar a criação e o descarte de um objeto String intermediário. Para aumentar o desempenho da concatenação de seqüência de caracteres repetida, um compilador Java pode usar a classe StringBuffer ou uma técnica semelhante para reduzir o número de objetos String intermediários criados pela avaliação de uma expressão.

Para tipos primitivos, uma implementação também pode otimizar a criação de um objeto wrapper, convertendo diretamente de um tipo primitivo em uma sequência.

A versão otimizada, na verdade, não fará uma conversão completa de String primeiro.

Esta é uma boa ilustração de uma versão otimizada usada pelo compilador, embora sem a conversão de uma primitiva, onde você pode ver o compilador mudando as coisas para um StringBuilder em segundo plano:

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


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

Gera isso - veja como os dois estilos de concatenação levam ao mesmo 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

Observando o exemplo acima e como o código de bytes baseado no código-fonte no exemplo fornecido é gerado, você poderá perceber que o compilador transformou internamente a seguinte instrução

cip+ciop; 

para dentro

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

Em outras palavras, o operador +na concatenação de strings é efetivamente uma abreviação para o StringBuilderidioma mais detalhado .

Leão
fonte
3
Muito obrigado, não estou familiarizado com os códigos de bytes jvm, mas gerou o código para String plus = cip + ciop; e String build = novo StringBuilder (cip) .append (ciop) .toString (); são iguais. e minha pergunta é que esta operação viola a orientação a objetos?
Pooya
7
Não, não faz. A sobrecarga do operador (como em C ++ e em algumas linguagens) tem algumas desvantagens e os designers de Java consideram um conceito um tanto confuso e o omitem do Java. Para mim, uma linguagem orientada a objetos deve ter os principais conceitos de herança, polimorfismo e encapsulamento que o Java possui.
Lion
2
sim, mas acho que esse operador foi sobrecarregado para a classe String
Pooya
3
Sim, a sobrecarga do operador é usada em Java para a concatenação do tipo String. No entanto, você não pode definir seu próprio operador (como em C ++, C # e algumas outras linguagens).
Lion
5
@ Pooya: na verdade, "int / int" vs. "int / float" está sobrecarregando o operador, então até C tem isso. O que C (e Java), no entanto , não possuem é sobrecarga de operador definida pelo usuário: a única coisa que define as diferentes maneiras pelas quais um operador pode ser usado (em C e Java) é a definição de linguagem (e a distinção é implementada no compilador). O C ++ difere na medida em que permite sobrecarga de operador definida pelo usuário (geralmente chamada de "sobrecarga de operador").
Joachim Sauer
27

É o recurso do compilador Java que verifica os operandos do +operador. E com base nos operandos, gera o código de bytes:

  • Para String, gera código para concat strings
  • Para números, ele gera código para adicionar números.

Isto é o que a especificação Java diz :

Os operadores + e -são chamados de operadores aditivos. AdditiveExpression: MultiplicativeExpression AdditiveExpression + MultiplicativeExpression AdditiveExpression - MultiplicativeExpression

Os operadores aditivos têm a mesma precedência e são sintaticamente associados à esquerda (eles agrupam da esquerda para a direita). Se o tipo de um operando de um +operador for String, a operação será concatenação de cadeia de caracteres.

Caso contrário, o tipo de cada um dos operandos do +operador deve ser um tipo conversível (§5.1.8) em um tipo numérico primitivo, ou ocorrerá um erro em tempo de compilação.

Em todos os casos, o tipo de cada um dos operandos do -operador binário deve ser um tipo conversível (§5.1.8) em um tipo numérico primitivo ou ocorrerá um erro em tempo de compilação.

Ramesh PVK
fonte
7
A citação da especificação não é relevante para esta questão.
Ernest Friedman-Hill
Este é o extrato "Se o tipo de um dos operandos de um operador + for String, a operação será concatenação de strings. Caso contrário, o tipo de cada um dos operandos do operador + deverá ser um tipo conversível (§5.1.8 ) para um tipo numérico primitivo ou ocorre um erro em tempo de compilação ". Você pode me dizer por que não é relevante?
Ramesh PVK
7
Não diz nada sobre como é implementado, qual é a questão. Acho que o pôster já entende que o recurso existe.
Ernest Friedman-Hill
14

Como a classe String substitui + operador?

Não faz. O compilador faz isso. Estritamente falando, o compilador sobrecarrega o operador + para operandos String.

Marquês de Lorne
fonte
6

Primeiro de tudo (+) está sobrecarregado, não substituído

A linguagem Java fornece suporte especial para o operador de concatenação de cadeias (+), que foi sobrecarregado para objetos Java Strings.

  1. Se o operando do lado esquerdo for String, ele funcionará como concatenação.

  2. Se o operando do lado esquerdo for Inteiro, ele funcionará como operador de adição

Pramod Kumar
fonte
3
(2) Se o operando esquerdo for um Número Inteiro, ele será retirado da caixa de seleção automática inte as regras normais de Java serão aplicadas.
Marquês de Lorne
2
As duas regras fornecidas abaixo da citação estão erradas: acredito que devam ser: duas primitivas (ou classes unboxable) = adição; pelo menos uma string = concatenação
mwfearnley
4

A linguagem Java fornece suporte especial para o operador de concatenação de cadeias (+) e para a conversão de outros objetos em cadeias. A concatenação de cadeias é implementada através da classe StringBuilder(ou StringBuffer) e de seu appendmétodo.

Jigar Pandya
fonte
4

O significado do +operador quando aplicado Stringé definido pelo idioma, como todos já escreveram. Como você não acha isso suficientemente convincente, considere o seguinte:

Ints, floats e doubles têm representações binárias diferentes e, portanto, adicionar duas entradas é uma operação diferente, em termos de manipulação de bits, do que adicionar duas entradas flutuantes: Para entradas, você pode adicionar pouco a pouco, carregando um pouco e verificando se há excesso; para carros alegóricos, você deve lidar com as mantissas e expoentes separadamente.

Portanto, em princípio, "adição" depende da natureza dos objetos que estão sendo "adicionados". Java define para Strings, bem como ints e floats (longs, doubles, ...)

alexis
fonte
3

O +operador geralmente é substituído por um StringBuilderem tempo de compilação. Verifique esta resposta para obter mais detalhes sobre esse assunto.

Escorpião
fonte
Se for esse o caso, existe algum motivo pelo qual o StringBuilder exista para uso público? Existem casos em que o +operador não é substituído por StringBuilder?
Cemre
2
A pergunta que você deseja fazer é "por que o operador + existe para uso público?", Porque essa é a abominação aqui. Quanto à sua outra pergunta, não sei exatamente, mas acho que não existe.
Escorpião
O compilador pode usar concat (), se houver apenas dois elementos. Além disso, o compilador falha ao substituir concat () pelo StringBuilder (ou usa vários StringBuilders e os anexa) quando o programador está construindo uma string em um longo código aninhado / em loop - o uso único e explícito de StringBuilder será melhor para o desempenho.
user158037