Concatenando cadeias nulas em Java

Por que o seguinte trabalho? Eu esperaria que uma NullPointerException fosse lançada.

 String s = null; s = s + "hello"; System.out.println(s); // prints "nullhello" 

Por que isso deve funcionar?

O JLS 5, Seção 15.18.1.1 JLS 8 § 15.18.1 “Operador de Concatenação de Cadeia +” , levando ao JLS 8, § 5.1.11 “Conversão de Cadeia” , requer que esta operação tenha sucesso sem falha:

… Agora apenas os valores de referência precisam ser considerados. Se a referência é nula, ela é convertida para a string “null” (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 object referenciado sem argumentos; mas se o resultado de chamar o método toString for nulo, a cadeia “null” será usada.

Como isso funciona?

Vamos dar uma olhada no bytecode! O compilador pega seu código:

 String s = null; s = s + "hello"; System.out.println(s); // prints "nullhello" 

e compila em bytecode como se você tivesse escrito isso:

 String s = null; s = new StringBuilder(String.valueOf(s)).append("hello").toString(); System.out.println(s); // prints "nullhello" 

(Você pode fazer isso sozinho usando javap -c )

Os methods de acréscimo de StringBuilder todos manipulam null bem. Neste caso, porque null é o primeiro argumento, String.valueOf() é invocado, uma vez que StringBuilder não possui um construtor que toma qualquer tipo de referência arbitrária.

Se você tivesse feito s = "hello" + s vez disso, o código equivalente seria:

 s = new StringBuilder("hello").append(s).toString(); 

onde, neste caso, o método append recebe o valor nulo e, em seguida, o delega para String.valueOf() .

Nota: A concatenação de strings é na verdade um dos raros lugares onde o compilador decide qual (is) otimização (s) executar. Como tal, o código “exato equivalente” pode diferir de compilador para compilador. Essa otimização é permitida pelo JLS, Seção 15.18.1.2 :

Para aumentar o desempenho da concatenação de cadeias repetidas, um compilador Java pode usar a class StringBuffer ou uma técnica semelhante para reduzir o número de objects String intermediários que são criados pela avaliação de uma expressão.

O compilador que eu usei para determinar o “código equivalente” acima foi o compilador do Eclipse, ecj .

Consulte a seção 5.4 e 15.18 da especificação da linguagem Java:

A conversão de string se aplica somente aos operandos do operador binário + quando um dos argumentos é uma String. Neste único caso especial, o outro argumento para o + é convertido em um String, e um novo String que é a concatenação das duas strings é o resultado do +. A conversão de string é especificada em detalhes na descrição do operador de concatenação de string +.

e

Se apenas uma expressão de operando for do tipo String, a conversão de sequência será executada no outro operando para produzir uma sequência em tempo de execução. O resultado é uma referência a um object String (recém-criado, a menos que a expressão seja uma expressão constante em tempo de compilation (§15.28)) que é a concatenação das duas cadeias de operandos. Os caracteres do operando esquerdo precedem os caracteres do operando direito na string recém-criada. Se um operando do tipo String for nulo, a cadeia “null” será usada em vez desse operando.

A segunda linha é transformada no seguinte código:

 s = (new StringBuilder()).append((String)null).append("hello").toString(); 

Os methods append podem manipular argumentos null .

Você não está usando o “null” e, portanto, você não recebe a exceção. Se você quiser o NullPointer, apenas faça

 String s = null; s = s.toString() + "hello"; 

E acho que o que você quer fazer é:

 String s = ""; s = s + "hello"; 

Esse é o comportamento especificado no método String.valueOf(Object) da API Java. Quando você faz concatenação, valueOf é usado para obter a representação de String . Existe um caso especial se o Object for null , caso em que a string "null" é usada.

public static String valueOf(Object obj)

Retorna a representação de string do argumento Object.

parameters: obj – um object.

Retorna:

se o argumento for nulo, então uma string igual a “null”; caso contrário, o valor de obj.toString () será retornado.